疯狂Java讲义读书笔记05 面向对象-下
Java8的增强的包装类
Java是面向对象的编程语言,但它也包含了8种基本数据类型,这八种基本数据类型不支持面向对象的编程机制
基本数据类型的数据也不具备“对象”的特性:没有成员变量、方法可以被调用,Java之所以提供者八种基本数据类型,主要是为了照顾传统程序员的编程习惯。
为了解决八种基本数据类型的变量不能当成Object类型变量使用的问题。
Java提出了包装类的概念,为8种基本数据类型分别定义了相应的引用类型,并称之为基本数据类型的包装类。

从表可以看出除了int和char有点特殊,其他就是将首字母大写就行。
jdk1.5之后提供了自动装箱和自动拆箱功能。
所谓的自动装箱就是可以把一个基本类型变量直接赋值给对应的包装类变量
自动拆箱则与之相反,允许直接把包装类对象直接赋值给一个对应的基本类型变量。

String转包装类,可以使用包装类的构造器或者包装类的parseXxx静态方法
包装类转string,可以使用String的valueof方法或者直接拼接空字符串
自动装箱就是可以直接把一个基本类型的值赋值给一个包装类的实例,在这种情况下可能会出现一些特别的情况

为什么会出现这种情况,这与Integer类的设计有关

系统将-128-127之间的整数缓存进了一个cache数组。
Java7为所有的包装类提供了一个静态的compare方法
Java对象都是object类的实例,都可以直接调用该类的方法,这些方法提供了处理Java对象的通用方法。
Object类提供的toString方法总是返回该对象实现类的“类名+@+hashcode”
这个值不能真正的实现自我描述,需要重写tostring方法。
Java程序中测试两个变量是否相等有两种方式,一种是利用==运算符,另一种是利用equals方法。
jvm常量池保证相同的字符串常量只有一个,不会产生多个副本。
当使用new String(“hello”)时,jvm会先使用常量池来管理“hello”直接量,再调用String类的构造器来创建一个新的String对象,新创建的对象String对象被保存再堆内存里。
也就是这个操作产生了两个字符串操作。
但很多时候我们只是想比较值相等。Object的equals方法和==效果一样
String类已经重写了Object的equals的方法,String的equals方法是判断两个字符串是否相等字符序列相同。
重写equals方法就是提供自定义的相等标准。
自反性:x.equals(x)一定返回true
对称性:y.equals(x)返回true,则x.equals(y)也返回true
传递性:对于x,y,z,如果x.equals(y)返回true,y.equals(z)为true,则x.equals(z)也返回true。
一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少此,返回的结构应该保持一致true,要么一直是false
许多语言都不允许通过对象访问类变量,对象只能访问实例变量;类变量必须通过类来访问。
实际上对象并不持有类变量,类变量是由该类持有的,变量。
类方法也是类成员的一种,类方法也是属于类的,通常直接使用类作为调用者来调用类方法,但也可以使用对象来调用类方法。与类变量相似,即使使用对象来调用类方法,其效果也与采用类来调用类方法完全一样。
null对象都可以访问类的类成员啊,妙啊。
如果一个null对象访问实例成员,将会引发nullpointerexception异常。
有一条重要规则要记住鸭
类成员不能访问实例成员。
如果一个类始终只能创建一个实例,则这个类被称为单例类。
一些特殊的场景下要求不允许自由创建该类的对象,而只允许为该类创建一个对象。

final值修饰变量时,表示该变量一旦获得了初始值就不可被改变,final即可以修饰成员变量也可以修饰形参。
final修饰的变量不可被改变,一旦获取了初始值,该final变量就不能被重新赋值。
由于final变量获得初始值之后不能被重新赋值,因此final修饰成员变量和修饰局部变量时有一定的不同。
对于final修饰的成员变量而言,一旦有了初始值就不能被重新赋值,如果即没有再定义成员变量时指定初始值也没有在初始化块和构造器中为成员变量指定初始化值,那么就是系统默认分配的null,0之类的值,无意义。因此
Java语法规定,final修饰的成员变量必须由程序员显示的指定初始值。
归纳起来,final修饰的类变量、实例变量能指定初始值的地方如下
类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在这两个地方指定。
实例变量:必须在非静态初始化块、声明该实例变量或者构造器中指定初始化值,而且只能在三个地方的其中之一。
实例变量不能在静态初始化块中指定初始值,因为静态初始化块是静态成员,不可以访问实例变量(非静态成员)
类变量不能在普通初始化块中指定初始值,因为类变量在类初始化阶段已经被初始化了,普通初始化块不能对其重新赋值。
与普通的成员变量不同的是,final成员变量必须由程序员显示初始化,系统不会对final成员进行隐式初始化。
系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。
如果final修饰的局部变量在定义时没有指定默认值,则可以在后面代码中对该final变量赋值初始值,但只能一次,不能重复赋值,如果final修饰的局部变量在定义时已经指定默认值,则后面代码不能再对该变量赋值。
当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。
但对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
对于一个final变量来说,只要满足三个条件,这个final变量就不再是一个变量,而是一个直接量
1、使用final修饰
2、定义变量时就指定了初始值
3、该初始值可以在编译时就确定。

上面程序中的粗体字定义了一个局部变量,当程序执行system.out.println(a),代码实际上转换成了system.out.println(5)
final修饰符的一个重要用途就是定义宏变量。

final方法
final方法修饰的方法不可以被重写。出于某些原因不想让子类重写父类的某些方法可以使用final修饰
object类里就有一个final方法,getclass。
final类不可以有子类,Math类就是一个final类
不可变类的意思就是创建该类的实例后,该实例的实例变量是不可变的。Java提供的8个包装礼盒String都是不可变类。
如果需要设计一个不可变类,尤其需要注意引用类型的成员变量,尤其要注意引用类型的成员变量,如果引用类型的成员变量是可变的,就必须要采取必要的措施来保护该成员变量所引用的对象不会被修改,这样才能创建真正的不可变类。
缓存实例的不可变类。
不可变类的实例状态不可改变,可以很方便被多个对象所共享。
如果程序经常需要使用相同的不可变类实例,则应该考虑缓存这种不可变类的实例。毕竟重复创建相同的对象没有太大的意义,而且增加系统开销,如果可能,应该将已经创建的不可变类的实例进行缓存。
缓存是软件设计中一个非常有用的模式,缓存的实现方式有很多种,不同的实现方式可能存在较大的性能差别。
使用valueof方法生成对象的时候,系统是否生成新的对象取决于数组内是否已经存在该对象。如果该数组中已经缓存了该类的对象,系统将不会重新生成对象。
如果某个对象需要频繁地重复使用,缓存该实例就利大于弊。如果只使用一次,重复使用的概率不大,那缓存该实例就弊大于利。
Java提供的Integer包装类,它就采用了相同的处理测量,如果new出来的话。每次就是全新的Integer对象,如果采用ValueOf方法来创建Integer对象,则会缓存该方法创建的对象。
Integer只缓存-128-127之间的数字。
抽象类
当编写一个类时,常常会定义一些方法,这些方法用于描述该类的行为方式,那么这些方法都有具体的方法体
但在某些情况下,某个父类只知道自己的子类应该包含增益的方法,但却不知道子类应该如何实现这些方法。
抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
总结就是抽象类可以包含抽象方法,但不能创建实例。
抽象类不能创建实例,只能当成父类来继承。
模板模式在面向对象的软件中很常见,其原理也很简单,实现也很简单。
抽象父类可以只定义需要使用的某些方法,把不能实现的部分抽象成抽象方法,留给子类去实现。
父类可能包含需要调用其他系列方法的方法,这些被调方法即可以由父类实现,也可以由子类实现。父类里提供的方法只是定义了一个通用算法,其实现也许并不完全由自身实现,而必须依赖子类的辅助。
一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。
接口定义是一种规范,接口里不能包含构造器和初始化块。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、或默认方法)、内部类(包含内部接口、枚举)定义
对比接口和类的定义方式,不难发现接口比类成员少了两种,而且接口的成员变量只能是静态常量,接口的方法只能是抽象方法,默认方法或类方法。
接口里所有成员都是public权限。


接口体现的是规范和实现分离的设计哲学,充分利用接口可以极好的降低程序各模块之间的耦合,可以提高系统的可扩展性和可维护性。
命令模式:通过一个Command接口就实现了让processArray类和具体处理行为的分离,程序使用COmmand接口代表了对数组的处理行为,但Command接口也没有提供真正的处理,只有等到调用ProcessArray对象的process方法时,才真正传入一个Command对象,才确定对数组的处理行为。
大部分时候类被定义成一个独立的程序单元。在某些情况下,也会把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类。
内部类成员可以直接访问外部类的私有数据。
匿名内部类适合用于创建那些仅需要一次使用的类。
非静态内部类里不能有静态方法、静态成员变量、静态初始化块。
Java还允许在接口里定义内部类,接口里定义的内部类默认使用public static修饰,也就是说,接口内部类只能是静态内部类。
如果为接口内部类指定访问控制符,只能指定public访问控制符。如果定义接口内部类时省略访问控制符,默认是public



内部类的类名不再是简单的由内部类的类名组成,实际上还把外部类的类名作为一个命名空间,作为内部类类名的限制。因此子类中的内部类和父类中的内部类不可能完全同名,即使它们的内部类类名相同,但因为外部类空间不同,所以就不可能完全同名,也就不可能重写。
局部内部类
如果将一个内部类放在方法里定义,则这个内部类就是一个局部内部类,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。也不能使用访问控制符和static

局部内部类是一个非常鸡肋的语法,很少使用。
匿名内部类适合创建一次使用的类,例如前面介绍命令模式所需要的Command对象,匿名内部类语法有点奇怪,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。
定义匿名内部类的格式

从上面定义可以看出匿名内部类必须继承一个父类或实现一个接口,但最多只能继承一个父类或实现一个接口。
匿名内部类不能是抽象类。
匿名内部类不能定义构造器。
匿名内部类访问局部变量必须使用final修饰。
Java的lambda表达式
lambda表达式允许使用简洁的代码来创建只要一个抽象方法的接口的实例
这种接口被称为函数式接口。

使用lambda表达式之后会简化

可以看出,当使用lambda表达式代替匿名内部类创建对象的适合,lambda表达式的代码块会代替抽象方法的方法体,lambda表达式就相当于一个匿名方法。
形参列表+箭头+代码块
形参列表可以省略形参类型,代码块只有一句代码可以省略花括号
形参只有一个可以省略圆括号
代码块只有一条语句可以省略return,自动返回值

由于lambda表达式的结果就是被当成对象,因此程序中完全可以使用lambda表达式进行赋值。

Runnable是Java本身提供的一个函数式接口。
可以看出lambda表达式实现的是匿名方法,因此它只能实现特定函数式接口中的唯一方法。这意味着lambda表达式有如下的两个限制
lambda表达式的目标类型必须是明确的函数式接口。
lambda表达式只能为函数式接口创建对象。lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口创建对象。
lambda表达式只有一条代码,还可以在代码块中使用方法引用和构造器引用

lambda表达式与匿名内部类的联系与区别
可以看出lambda表达式是对匿名内部类的一种简化,因此它可以部分取代匿名内部类。
与匿名内部类相似的是,由于lambda访问了局部变量,因此该局部变量相当于有一个隐式的final修饰,因此同样不允许对book局部变量重新赋值
区别:
匿名内部类可以为任意接口创建实例,不管接口包含包含多少个抽象方法,只要匿名内部类实现所有抽象方法即可,但lambda表达式只能为函数式接口创建实例。
匿名内部类可以为抽象类甚至普通类创建实例。但Lambda只能为函数式接口创建实例。
匿名内部类实现的抽象方法的方法体允许调用接口定义中的默认方法,但lambda表达式代码块中不允许调用接口定义的默认方法。
枚举类
在某些情况下,一个类的对象是有限且固定的例如季节。
枚举类可以实现一个或多个接口。使用enum定义的枚举类默认继承了Enum类而不是Object类,因此不能显式继承其他父类。其中Enum类实现了Serializable接口和Comparable接口。
使用enum定义、非抽象的枚举类会默认使用final修饰,枚举类不能派生子类
枚举类的构造器只能使用private。
枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。列出这些实例的时候,系统会自动添加public static final修饰
枚举类默认提供一个values方法,方便遍历

垃圾回收机制只负责回收内存中的对象不会回收任何物理资源(数据库连接,网络IO)
程序无法精确控制垃圾回收的运行,垃圾回收会在适合的时候运行。当对象永久性的失去了引用后系统就会在合适的时候回收它所占的内存。
在垃圾回收机制回收任何对象之前,总会先调用它的finalize方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消。
当一个对象在堆内存中运行时,根据引用变量所引用的状态,可以分成三种
可达状态、可恢复状态和不可达状态。

浙公网安备 33010602011771号