面向对象(下)

一、Java8增强的包装类

    为8种基本数据类型分别定义了相应的引用类型,并称之为基本数据类型的包装类。

    JDK1.5提供了自动装箱和自动拆箱功能。自动装箱:将一个基本类型变量直接赋给对应的包装类变量,或者赋给Object变量;自动拆箱:将包装类对象直接赋给一个对应的基本类型变量。

    包装类可以实现基本类型变量和字符串之间的转换,把字符串类型的值转换为基本类型的值有两种方式:利用包装类提供的parseXxx静态方法;利用包装类提供的Xxx(String s)构造器。String类提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串。

public class Primitive2String
{
	public static void main(String args[])
	{
		String inStr = "123";
		int it1 = Integer.parseInt(intStr);
		int it2 = new Integer(intStr);
		System.out.println(it2);
		String ftstr = String.valueOf(2.345f);
		String dbstr = String.valueOf(3.344);
		String boolstr=String.valueOf(true);
	}
}
    如果希望把基本类型变量转换成字符串,可以将基本类型变量和“”进行连接原酸,系统会自动把基本类型变量转换成字符串。

辨析

Integer ina=2;
Integer ina=2;
System.out.println("两个2自动装箱后是否相等:"+(ina==inb));//输出true
Integer biga=2;
Integer biga=2;
System.out.println("两个128自动装箱后是否相等:"+(biga==bigb));//输出false
    原因:系统把一个-128~127之间的整数自动装箱成Integer实例,并放入一个名为cache的数组中缓存起来。如果以后把一个-128~127之间的整数自动装箱成一个Integer实例时,实际上是直接指向对应的数组元素,因此-128~127之间的同一个整数自动装箱成Integer实例时,永远都是引用cache数组的同一个数组元素,所以它们全部相等;但每次把一个不在-128~127范围内的整数自动装箱成Integer实例时,系统重视重新创建一个Integer实例。

    Java7增强了包装类的功能,Java7为所有的包装类都提供了一个静态的compare(xxx val1 , xxx val2)方法,这样开发者就可以通过这一方法来比较两个基本类型之的大小。

二、处理对象

(1)打印对象和toString方法

     toString方法是一个特殊的方法:它是一个“自我描述”方法,该方法通常用于实现这样一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用以告诉外界该对象具有的状态信息。Object类提供的toString()方法总是返回fail对象实现类的“类名+@+hashCode”值,这个返回值并不能真正实现“自我描述”的功能,因此如果用户需要自定义类能实现“自我描述”的功能,就必须重写Object类的同String()方法。

(2)==和equals方法

    当使用==来判断两个变量是否相等,如果两个变量是基本类型变量,且都是数值类型(不一定要求数据类型严格相同),则只要两个变量的值相等,就将返回true。但对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true。==不可用于比较类型上没有父子关系的两个对象。

    “hello”直接量和new String(“hello”)的区别:直接使用形如“hello”的字符串直接量时,JVM将会使用常量池来管理这些字符串;当使用new String(“hello”)时,JVM会先使用常量池来管理“hello”直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中。

    equals()方法是Object类提供的一个实例方法,因此所有引用变量都可调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与使用==运算符没有区别。String已经重写Object的equals()方法,String的equals()方法判断两个字符串相等的标准是:只要两个字符串所包含的字符序列相同,就返回true,否则返回false。

三、类成员

    类变量既可通过类来访问,也可通过类的对象来访问。但通过类的对象来访问类变量时,实际上并不是访问该对象所拥有的变量。

    null对象可创建以访问它所属类的类成员,但如果一个null对象访问实例成员(包括实例变量和实例方法)将会引发NullPointerException异常。

    如果一个类始终只能创建一个实例,则这个类被称为单例类。为了避免其他类自由创建该类的实例,应该把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。一旦把该类的构造器隐藏起来,就需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰:因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类。该类还必须缓存已经创建的对象,该成员变量需要被上面的静态方法访问,故该成员变量必须使用static修饰。

四、final修饰符

    final关键字可用于修饰类、变量和方法,用于表示修饰的类、方法和变量不可改变。final修饰的变量一旦获得了初始值,该final变量的值就不能被重新赋值

    final修饰的成员变量必须由程序员显示地指定初始值。类变量:必须在静态初始化块中指定初始值或在声明该类变量时指定初始值;实例变量:必须在非静态初始化块、声明该实例变量或构造器中指定初始值。

    final修饰形参时,因为形参在调用方法时,由系统根据传入的参数来完成初始化,因此使用final修饰的形参不能被赋值。

    当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。

    final修饰符的一个重要用途就是定义“宏变量”。当定义final变量时就为该变量指定了初始值,而且该初始值可以在编译时就确定下来,那么这个final变量本质上就是一个“宏变量”,编译器会把程序中所有用到该变量的地方直接替换成该变量的值。 对于实例变量而言,既可以在定义该变量时赋初始值,也可以在非静态初始化块、构造器中对它赋初始值,在这三个地方制定初始值的效果基本一样。但对于final实例变量而言,只有在定义该变量时指定初始值才会有“宏变量”的效果。

    final修饰的方法不可被重写,但并不是不能被重载。对于一个private方法,因为它仅在当前类中课件,其子类无法访问该方法,所以子类无法重写该方法,如果子类中定义一个与父类private方法有相同方法名、相同形参列表、相同返回值类型的方法,也不是方法重写,只是重新定义了一个新方法。因此,即使使用final修饰一个private访问权限的方法,依然可以在其子类中定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。

    final修饰的类不可以有子类

    不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。如果需要设计一个不可变类,尤其要注意其引用类型的成员变量,如果引用类型的成员变量的类是可变的,就必须采取必要的措施来保护该成员变量所引用的对象不会被修改,这样才能创建真正的不可变类。

    缓存实例的不可变类:一个例子是Java提供的java.lang.Integer类,如果采用new构造器来创建Integer对象,则每次返回全新的Integer对象;如果采用valueOf()方法来创建Integer对象,则会缓存该方法创建的对象。

五、抽象类

     抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。抽象方法不能有方法体,抽象类不能被实例化

     抽象方法和空方法体的方法不是同一个概念

     final和abstract永远不能同时使用;static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法;abstact关键字修饰的方法必须被其子类重写才有意义,否则这个方法将永远不会有方法体,因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。

     abstract不能用于修饰成员变量,不能用于修饰局部变量, 即没有抽象变量、没有抽象成员变量等说法;abstract也不能用于修饰构造器,没有抽象构造器,抽象类里定义的构造器只能是普通构造器。

六、接口

    抽象类是从多个类种抽象出来的模板,如果将这种抽象进行得更彻底,则可以提炼出一种更加特殊的“抽象类”,即接口(interface)。接口不提供任何实现

    对接口的修饰符可以是public或者省略,如果省略了public访问控制符,则默认采用包权限访问控制符,即只有在相同包结构下才可以访问该接口。一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

     接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法或默认方法)、内部类(包括内部接口、枚举)定义。

     在接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用者三个修饰符来修饰,且接口里没有构造器和初始化块,因此接口里定义的成员变量只能在定义时指定默认值。

     定义接口里的普通方法时不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract修饰。接口里的普通方法不能有方法实现(方法体),但类方法、默认方法都必须有方法实现。

     默认方法必须使用default修饰,该方法不能使用static修饰,无论程序是否指定,默认方法总是使用public修饰。

     类方法必须使用static修饰,该方法不能使用default修饰,无论程序是否指定,类方法总是public修饰。

     一个Java源文件里最多只能有一个public接口,如果一个Java源文件里定义了一个public接口,则该源文件的主文件名必须与该接口名相同。

    接口完全支持多继承,即一个接口可以有多个直接父接口。一个类可以实现一个或多个接口,继承使用extends关键字,实现则使用implements关键字。实现接口方法时,必须使用public访问控制修饰符,因为接口里的方法都是public的,而子类(相当于实现类)重写父类方法时访问权限职能更大或者相等,所以实现类实现接口里的方法时只能使用public访问权限

七、内部类

     内部类比外部类可以多使用三个修饰符:private、protected、static,外部类不可以使用这三个修饰符。非静态内部类不能拥有静态成员。外部类的上一级程序单元是包,所以它只有两个作用域,同一个包内和任何位置,因此只需两种访问权限:包访问权限和公开访问权限,正好对应省略访问控制符和public访问控制符。内部类的上一级程序单元是外部类,它就具有四个作用域:同一个类、同一个包、父子类和任何位置,因此可以使用4种访问控制权限。

    非静态内部类的成员可以访问外部类的private成员,但反过来不成立。非静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显式创建非静态内部类对象来调用访问其实例成员。

    不允许在外部类的静态成员中直接使用非静态内部类,不允许在非静态内部类里定义静态成员。

    如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。静态内部类可以包含静态成员,也可以包含非静态成员。静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。外部类依然不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。

     非静态内部类寄生在外部类的实例中,静态内部类寄生在外部类的类本身中

     匿名内部类适合创建那种只需要一次使用的类。匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。匿名内部类不能是抽象类,也不能定义构造器,但匿名内部类可以定义初始化块。如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,此处相似指的是拥有相同的形参列表。

    定义匿名内部类的格式如下:new 实现接口() | 父类构造器(实参列表){  // 匿名内部类的类体部分  }

    从Java8开始,匿名内部类、局部内部类允许访问非final的局部变量,如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰。

八、Lambda表达式

    Lambda表达式的主要作用是代替匿名内部类的繁琐语法,由三部分组成:形参列表,箭头(->),代码块。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。

    Lambda表达式的目标类型必须是“函数式接口”,函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法

    如果Lambda表达式的代码块只有一条代码,程序可以省略Lambda表达式中代码块的花括号。如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。

Lambda表达式和匿名内部类的区别

i) 匿名内部类可以为任意接口创建实例,不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可,但Lambda表达式只能为函数式接口创建实例。

ii) 匿名内部类可以为抽象类甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例。

iii) 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法,但Lambda表达式的代码块不允许调用接口中定义的默认方法(但它创建的对象可以直接调用从接口中继承的默认方法)。

九、枚举类

    使用enum关键字,可以定义枚举类。枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,其中java.lang.Enum类实现了java.lang.Serializable接口和java.lang.Comparable接口。使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private访问控制符。枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例,列出这些实例时,系统会自动添加public static final修饰,无需显式添加

    枚举类默认提供了一个value()方法,该方法可以很方便地遍历所有的枚举值

十、对象与垃圾回收

    当一个对象在堆内存中运行时,根据它被引用变量所引用的状态,可以把它所处的状态分成如下三种:可达状态、可恢复状态、不可达状态。一个对象可以被一个方法的局部变量引用,也可以被其他类的类变量引用,或被其他对象的实例引用,当某个对象被其他类的类变量强制引用时,只有该类被销毁后,该对象才会进入可恢复状态,当某个对象被其他对象的实例变量引用时,只有当该对象被销毁后,该对象才会进入可恢复状态。

    强制系统垃圾回收的方法:调用System类的gc()静态方法,System.gc();调用Runtime对象的gc()实例方法:Runtime.getRuntime().gc()。运行java命令时指定-verbose:gc选项,可以看到每次垃圾回收后的提示信息。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值