《Java核心技术(卷I)》学习笔记 (一)
Java核心技术(卷I)学习笔记
第三章 Java基本程序设计结构
[2019-02-27]
在Java中使用"final"指示常量,一般来说变量名使用全大写字母,且该变量只可以赋值一次。在main方法外使用"static final"定义类常量,可以在同一个类中所有的方法中使用,若定义为"public"则可以再其它类中使用。
Integer是整数类,已封装,而int是基本的数据类型。
public static strictfp void main.........该方法中所有计算使用严格的浮点计算,中间结果不会截断。
常用数学函数:
Math.sin Math.cos Math.tan Math.atan Math.atan2 Math.exp Math.log Math.log10 Math.PI Math.E
32位的int型数字如果移位运算需要mod32,1<<37 <=> 1<<5。
Java中可使用"+"连接两个字符串,当某个字符串与非字符串类型P连接时,会自动将P转化成字符串类型。
Java中不可直接修改字符串某个位置具体的字符,但是可以修改整个字符串变量,例如,str = str.substring(0,3) + "p!",可将原来的str(内容是Hel)改为"Help!"。
String类对象被称为不可变对象。
检测两个字符串(变量)是否相等可使用s.equals(p)。
(P50)具体参见Java String类 API文档
有时要检查一个字符串既不是null 也不为空串,这种情况下就需要使用以下条件: if (str != null && str.length() != 0)
码点与代码单元部分暂时略过
使用字符串构建器(StringBuilder类)来构建多个短小字符(串)的拼接字符串,最后使用builder.toString方法来得到一个完整的字符串。
(卷I)【P58】 printf 格式化输出控制字符,【P60】时间输出格式,文件读写等。
在C++ 中, 可以在嵌套的块中重定义一个变量。在内层定义的变量会覆盖在 外层定义的变量。这样, 有可能会导致程序设计错误, 因此在Java 中不允许这样做。
Java中的switch-case结构与C语言中相同:
switch (choice) { case 1: break; case 2: break; case 3: break; case 4: break; default: // bad input break; }
【P76-78】Java大数计算 BigInteger(整数)和BigDecimal(浮点数)
public class Test { public static void main(String[] args) { //计算1000! BigInteger ans = BigInteger.valueOf(1); for(int i=1;i<=1000;i++){ BigInteger input = BigInteger.valueOf(i); ans = ans.multiply(input); } System.out.print(ans); } }
Java中数组声明方式有二:Type[] a或者Type a[],一般使用前者,只有Type[] a = new Type[N],才算声明了一个长度为N的数组。多维数组的声明相同,并且可以声明不规则长度的数组,先初始化行数,在分配每一行的长度大小。【P89】
int[] small Primes = { 2, 3, 5, 7, 11, 13 };//不使用new来初始化
small Primes = new int[] { 17, 19, 23, 29, 31, 37 };//重新初始化
--匿名数组???
copyOf(array,arraylength)数组拷贝。
for each循环,功能与C++、Python相同。
命令行参数 "String[] agrs",网上有详细说明。
[2019-02-28]--还未UP
第四章 对象与类
OOP三特性:对象的行为、状态和标识。
类之间三大关系:
依赖(uses-a)--如果一个类的方法操纵另一个 类的对象,我们就说一个类依赖于另一个类
聚合(has-a)--聚合关系意味着类A 的对象包含类B 的对象
集成(is-a)--用于表示特殊与一般关系。
UML ( Unified Modeling Language , 统一建模语言)绘制类图(软工未选修)。
LocalDate类(感觉好奇怪)
只访问对象而不修改对象的方法有时称为访问器方法(accesser method)
会对对象造成影响,改变其状态的方法叫做更改器方法(mutator method)
import java.time.*; /** * Orion233 * print this month‘s calendar */ public class Test { public static void main(String[] args) { LocalDate date = LocalDate.now(); int month = date.getMonthValue(); int today = date.getDayOfMonth() ; date = date.minusDays(today - 1); // Set to start of month DayOfWeek weekday = date.getDayOfWeek() ; int value = weekday.getValue(); System.out.println("Mon Tue Wed Thu Fri Sat Sun" + value) ; for (int i = 1; i < value; i++) System.out.print(" "); while (date.getMonthValue() == month) { System.out.printf("%3d", date.getDayOfMonth()) ; if (date.getDayOfMonth() == today) System.out.print("*"); else System.out.print(" ") ; date = date.plusDays(1) ; if (date.getDayOfWeek() .getValue() == 1) System.out.println(); } if (date.getDayOfWeek() .getValue() != 1) System.out.println() ; } }
Java构造器(所有的Java 对象都是在堆中构造的):
•构造器与类同名 •每个类可以有一个以上的构造器 •构造器可以有0 个、1 个或多个参数 •构造器没有返回值 •构造器总是伴随着new 操作一起调用
隐式参数与显式参数
this标识隐式参数,这样可以更好地实例域与局部变量明显地区分开来。
成员变量:
①成员变量定义在类中,在整个类中都可以被访问。
②成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中。
③成员变量有默认初始化值。
局部变量:
①局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。
②局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。
③局部变量没有默认初始化值
进行局部运算的时候,局部变量会屏蔽全局变量。 Example: Class A { int a = 1; public static void main(String[] args) { A c = new A(); a = 2; System.out.println(a); System.out.println(c.a); } }
public class Person { private int age=1000;//定义成员变量,年龄为1000 public void setAge(int age) { age=age; System.out.println("方法内的年龄"+age); } public void sayHello() { System.out.println("我的年龄是"+age+"."); } } ...... Person p=new Person(); p.setAge(20); p.sayHello(); ...... Results: //方法内的年龄20 //我的年龄是1000. /* 如果不同名,那么方法内的变量名代表成员变量;如果同名,那么方法内的变量名就只表示局部变量了,和成员变量一毛钱关系都没有了。 所以,首先当我们创建了一个Person对象p,在创建对象的时候就已经完成了成员变量的初始化了。成员变量age的初始值是1000。 当我们p.setAge(20)的时候,其实这个20只在setAge这个方法内起作用,所以输出了方法内年龄20,执行完这句话后,20就被销毁了。 然后执行sayHello,它里面的age代表的是成员变量的值,所以依旧是1000。 所以这里应该使用this表示当前对象。 this.age在这里具体表示p对象的age(即p对象的成员变量age)的值是20。 */
方法可以访问所属类的私有特性( feature ), 而不仅限于访问隐式参数的私有特性。一个方法可以访问所属类的所有 对象的私有数据。
另参考:
可以将实例域定义为final。构建对象时必须初始化这样的域。也就是说, 必须确保在每一个构造器执行之后, 这个域的值被设置, 并且在后面的操作中, 不能够再对它进行修改。例如, 可以将Employee 类中的name 域声明为final , 因为在对象构建之后, 这个值不会再被修改, 即没有setName 方法。
PS:StringBUilder的例子不大一样,他可以修改,见【P112】
[2019-03-01]
如果将域定义为static, 每个类中只有一个这样的域。而每一个对象对于所有的实例域 却都有自己的一份拷贝。
参考资料:Java中的static关键字解析
静态变量、静态常量
静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
--What is a field in java?
--A field is an attribute. A field may be a class’s variable, an object’s variable, an object’s method’s variable, or a parameter of a function.
field,域是一种属性,可以是一个类变量,一个对象变量,一个对象方法变量或者是一个函数的参数。(补充,class's variables,类的实例变量和静态变量称为class's variables,类属变量,也称类变量或数据域,其实翻译成属性也可以,类属性,听起来不会怪怪的,来自百度百科)。
可以使用对象调用静态方法e.g.可以用 harry.getNextId( ) 代替 Employee.getNextId( ),但是建议使用类名,而不是对象来调用 静态方法。
在下面2种情?下使用静态方法:
<1> 一个方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如:Math.pow )
<2> 一个方法只需要访问类的静态域(例如:Employee.getNextld)
P132 4.4.4 工厂方法
博客出处:http://blog.csdn.net/cgwshxs/archive/2008/12/05/3455136.aspx
静态工厂方法讲解<一> 创建类的实例的最常见的方式是用new语句调用类的构造方法。在这种情况下,程序可以创建类的任意多个实例,每执行一条new语句,都会导致Java虚拟机的堆区中产生一个新的对象。假如类需要进一步封装创建自身实例的细节,并且控制自身实例的数目,那么可以提供静态工厂方法。 例如Class实例是Java虚拟机在加载一个类时自动创建的,程序无法用new语句创建java.lang.Class类的实例,因为Class类没有提供public类型的构造方法。为了使程序能获得代表某个类的Class实例,在Class类中提供了静态工厂方法
forName(String name),它的使用方式如下: Class c=Class.forName("Sample"); //返回代表Sample类的实例
静态工厂方法与用new语句调用的构造方法相比,有以下区别。
(1)构造方法的名字必须与类名相同。这一特性的优点是符合Java语言的规范,缺点是类的所有重载的构造方法的名字都相同,不能从名字上区分每个重载方法,容易引起混淆。静态工厂方法的方法名可以是任意的,这一特性的优点是可以提高程序代码的可读性,在方法名中能体现与实例有关的信息。例如Gender类有两个静态工厂方法:getFemale()和getMale()。
Gender.java public class Gender{ private String description; private static final Gender female=new Gender("女"); private static final Gender male=new Gender("男"); private Gender(String description){this.description=description;} public static Gender getFemale(){ return female; } public static Gender getMale(){ return male; } public String getDescription(){return description;} }
这一特性的缺点是与其他的静态方法没有明显的区别,使用户难以识别类中到底哪些静态方法专门负责返回类的实例。为了减少这一缺点带来的负面影响,可以在为静态工厂方法命名时尽量遵守约定俗成的规范,当然这不是必需的。目前比较流行的规范是把静态工厂方法命名为valueOf或者getInstance。
valueOf:该方法返回的实例与它的参数具有同样的值,例如: Integer a=Integer.valueOf(100); //返回取值为100的Integer对象
从上面代码可以看出,valueOf()方法能执行类型转换操作,在本例中,把int类型的基本数据转换为Integer对象。
getInstance:返回的实例与参数匹配,例如:
//返回符合中国标准的日历
Calendar cal=Calendar.getInstance(Locale.CHINA);
(2)每次执行new语句时,都会创建一个新的对象。而静态工厂方法每次被调用的时候,是否会创建一个新的对象完全取决于方法的实现。
(3)new语句只能创建当前类的实例,而静态工厂方法可以返回当前类的子类的实例,这一特性可以在创建松耦合的系统接口时发挥作用。
静态工厂方法最主要的特点是:每次被调用的时候,不一定要创建一个新的对象。利用这一特点,静态工厂方法可用来创建以下类的实例。 单例类:只有惟一的实例的类。 枚举类:实例的数量有限的类。 具有实例缓存的类:能把已经创建的实例暂且存放在缓存中的类。 具有实例缓存的不可变类:不可变类的实例一旦创建,其属性值就不会被改变。
静态工厂方法讲解<二> 实例化对象的方法:
1.构造函数。
2.静态工厂方法。(我不常用,但JAVA平台库有好多例子)
对于构造函数,是新建对象是自动调用的方法,返回该对象,主要用于初始化类的成员字段。而另外一种构建对象的方式是采用静态工厂方法。静态工厂方法与别的静态方法没有什么区别,只不过该方法产生的类对象,不做其他事情,如我们常用的Sington单态模式。使用静态工厂方法主要有以下优点:
第一:静态工厂方法可以突破构造函数不能自由命名的限制,对于不同的工厂方法可以采用不同的会意的名字,是程序具有更好的可读性。JAVA平台库的java.text.Format的子类NumberFormat就有getInstance() , getPrecentInstance() , getCurrencyInstatnce()等静态方法,通过不同的名字产生特定的对象。
第二:静态工厂方法是用来产生对象用的,至于产生什么类型的对象没有限制,这就意味这只要返回原返回类型或原返回类型的子类型都可以,这样就加大了程序设计和使用的灵活行,如java.util集合框架就采用了这种优势,这样就可以更好达到封装的目的,降低API的数目和用户的使用难度,java.util.Connections是集合框架的辅助类用于对原有的集合类进行进一步封装,产生一些同步的集合,不可修改的视图。都是采用静态工厂方法实现的,至于方法内部的实现类就完全别封装了。也迫使我们使用接口编程。
第三:静态工厂方法所创建的对象可以在编译时不存在,动态创建对象,采用放射,类似SPRING的IOC容器方转。最典型的例子就是spi服务提供者框架,Service Provider Iframe 是一种用于在运行时刻产生对象的框架,达到对象的创建与使用分离,是对象的客户和对象之间解耦,增加程序的灵活性和可扩展性。既然spi可以动态创建对象,那么采用什么机制来创建什么对象,创建对象的依据是什么了,spi必须一种统一的注册机制,对于要创建的对象,应该在XML文件中配置,到时候,只要提供一个字符串,就可以凭借该字符串来创建配置的对象。简单实现如下:
class SPITest{ private static Map SPIMap = new HashMap(); private void initSPIMapIfNeccury(){ if(SPIMap == null){ SPIMap = new HashMap(); } //initMap user the sepecify XML //the map key is a beanName ,the value is a Object which config by XML } public Object getInstace(String beanName){ Object result = null; try{ if(SPIMap.containsKey(beanName)){ result = SPIMap.get(beanName); } else{ throw new Exception(" please config the xml file ,this bean is not exists!"); } }catch(Exception exce){ exce.printStackTrace(); } return result; } }
可以明显看出待创建的对象具体创建哪个对象,在编译时并不知道,只有在运行时刻才知道,将对象的创建工作推迟到运行时,这即是优点,有是缺点,失去了编译检查的功能。
但静态工厂方法又有缺点:
第一:如果将要创建的对象的构造方法是私有的或是default的,就有可能不能创建该对象。
第二:采用构造函数实例化对象,是语言的规范,而静态工厂方法与其他的静态方法没有区别,就增加了用户使用的区别。但这可以尽量采用一些家喻户晓的名字解决,让用户看到改名字就知道该方法是静态工厂方法。如getInstance()。
未完待续...
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!