疯狂JAVA讲义面向对象下(第六章)学习笔记

本文详细总结了Java面向对象编程中的一些重要概念,包括字符串转换、基本类型与字符串的转换、比较方法、toString()、equals()和hashCode()、final关键字的使用、常量池、equals()与==的区别、静态与抽象的结合、接口与抽象类的特性、内部类与匿名内部类的用法、Lambda表达式以及枚举类的相关知识。内容覆盖了Java中易混淆和关键的知识点,适合进阶学习。
摘要由CSDN通过智能技术生成

一:简单零碎的知识汇总

1)

2)将字符串转换为基本类型的值主要有两种方法:

      1.利用包装类的parseXxx(String s)的静态方法(除Character之外的所有包装类都提供了该方法)。

      2.利用包装类的valueOf(String s)的静态方法。

3)将基本数据类型转换成字符串可以使用String类的valueOf()静态方法。也可以将其与""进行连接运算。

4)所有包装类都有一个静态的compare(xxx val1,xxx val2)方法,这个方法可以比较两个基本类型值的大小,包括比较两个boolean类型(true>false)。

5)toString()方法是Object类里的一个实例方法,所有的java类都是Object类的子类,因此所有类都具有toString()方法。如下两行代码输出相同:

6)==运算符比较两个基本数据类型时,只要两个变量的值相同(不一定要求数据类型严格相同),就会返回true。

     ==运算符比较两个引用类型变量时,只有它们指向同一个对象时才返回true,==不可用于比较类型上没有父子关系的两个对象。

7)关于常量池。

8)equals()方法是Object类所有的一个实例方法,因此所有引用变量都可调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与==没有区别,同样要求两个引用变量指向同一个对象才会返回true。如果希望采用自定义的相等标准,则可采用重写equals()方法来实现。String类已经重写了Object的equals()方法

9)static不能修饰构造器,static修饰的类成员属于整个类,不属于单个实例。

10)final变量用于修饰类,变量和方法。

11)   final修饰的成员变量必须由程序员显式初始化:

          1.final修饰的类变量必须在静态初始化块或声明该变量时指定初始值,而且只能在两个地方其中之一指定。

          2.final修饰的实例变量必须在非静态初始化块,声明该变量时或构造器中指定初始值,而且只能在三个地方之一指定。

        注意体会如下代码:

public class FinalVariableTest
{
	// 定义成员变量时指定默认值,合法。
	final int a = 6;
	// 下面变量将在构造器或初始化块中分配初始值
	final String str;
	final int c;
	final static double d;
	// 既没有指定默认值,又没有在初始化块、构造器中指定初始值,
	// 下面定义的ch实例变量是不合法的。
	// final char ch;
	// 初始化块,可对没有指定默认值的实例变量指定初始值
	{
		//在初始化块中为实例变量指定初始值,合法
		str = "Hello";
		// 定义a实例变量时已经指定了默认值,
		// 不能为a重新赋值,因此下面赋值语句非法
		// a = 9;
	}
	// 静态初始化块,可对没有指定默认值的类变量指定初始值
	static
	{
		// 在静态初始化块中为类变量指定初始值,合法
		d = 5.6;
	}
	// 构造器,可对既没有指定默认值、有没有在初始化块中
	// 指定初始值的实例变量指定初始值
	public FinalVariableTest()
	{
		// 如果在初始化块中已经对str指定了初始化值,
		// 构造器中不能对final变量重新赋值,下面赋值语句非法
		// str = "java";
		c = 5;
	}
	public void changeFinal()
	{
		// 普通方法不能为final修饰的成员变量赋值
		// d = 1.2;
		// 不能在普通方法中为final成员变量指定初始值
		// ch = 'a';
	}
	public static void main(String[] args)
	{
		FinalVariableTest ft = new FinalVariableTest();
		System.out.println(ft.a);
		System.out.println(ft.c);
		System.out.println(ft.d);
	}
}

11)final成员变量在显式初始化前不能直接访问,但可以通过方法进行访问。

12)final局部变量如果定义时没有指定默认值,则可以在后面代码对对其进行一次赋值。如果已经指定默认值,则不能再对该变量赋值。final修饰形参时,因为形参在调用方法时,由系统根据传入的参数来完成初始化,因此使用final修饰的形参不能被初始化。

13)final修饰基本类型变量和引用类型变量的区别。 

        final修饰基本类型变量时,基本类型变量不能被改变。但对于引用类型变量,它保存的仅仅是一个引用,final保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以改变。

14)final修饰的方法不可被重写,可以被重载,private修饰的方法不可被继承。

15)final修饰的类不可以有子类,例如java.lang.Math类就是一个final类,它不可以有子类。

16)java提供的8个包装类和java.lang.String类都是不可变类,当创建它们的实例后,其实例的实例变量不可改变。

17)由抽象方法的类必须被定义为抽象类,抽象类可以没有抽象方法。

18)抽象方法没有花括号,例如:public abstract void test();。形如public void test(){ }的方法是一个普通方法,它已经定义了方法体,只是方法体为空,因此这个方法不能被abstract修饰。

19)final和abstract永远不能一起使用。修饰方法时,static和abstract不能一起使用,即没有所谓类抽象方法,但它们可以同时修饰内部类。修饰方法时,private和abstract也不能一起使用。

20)一个接口可以有多个直接父接口,但接口只能继承接口,不能继承类。

21)由于接口定义的是一种规范,因此接口不能含有构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法,默认方法或私有方法),内部类(包括内部接口,枚举)定义。

22)接口定义的是多个类公共的共同行为规范,因此接口里的常量,方法,内部类和内部枚举都是public访问权限。定义接口成员时,可以省略访问控制修饰符,如果指定访问控制修饰符,则只能使用public访问控制修饰符。

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

24)接口中定义的方法只能是抽象方法,类方法,默认方法和私有方法,因此如果不是定义类方法,默认方法和私有方法,系统将自动为普通方法添加abstract修饰符;接口里的普通方法,不管是否有public abstract修饰符,总是使用public abstract来修饰。接口里的普通方法不能有方法实现(方法体);但类方法,默认方法和私有方法必须有方法实现(方法体)。

25)接口里的定义的内部类,内部接口和枚举默认都采用public static两个修饰符,不论定义时是否指定这两个修饰符。

26)接口中定义默认方法,要用default修饰。

27)一个类可以继承一个父类,并同时实现多个接口,implements必须放在extends部分之后。

28)内部类成员可以直接访问外部类的私有数据,但外部类不能直接访问内部类的实现细节,如内部类的成员变量。

29)内部类比外部类可以多使用三个修饰符:private,protected,static。非静态内部类,不能拥有静态成员

30)如果外部类成员变量,内部类成员变量与内部类方法的局部变量同名,则可通过使用this,外部类名.this作为限定来区别

31)静态内部类不能直接访问外部类的实例成员,但可以通过new 外部类().成员 的方式访问。

32)在外部类以外的地方创建非静态内部类的对象使用如下类似语法:

33)在外部类以外的地方创建静态内部类的对象使用如下类似语法:

34)

35)匿名内部类的定义格式如下:

匿名内部类必须实现一个接口或者继承一个类,但最多只能实现一个接口或继承一个类。

关于匿名内部类还有如下两条规则:

1.匿名内部类不能是抽象类。

2.匿名内部类不能定义构造器。

最常见的创建匿名内部类的方式是需要创建某个接口类型的对象,如下程序所示。

当通过实现接口创建匿名内部类时,由于匿名内部类不能显式创建构造器,因此匿名内部类只有一个隐式的无参构造器,故new 接口名后的括号内不能传入参数。

但如果通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造器,即拥有相同的参数列表。

36)Java要求被局部内部类,匿名内部类访问的局部变量必须使用final修饰,Java8之后,这个限制被取消:如果局部变量被匿名内部类访问,那么该局部变量相当于自动使用了final修饰。例如下:

如果在①号代码后增加如下代码,则导致编译报错:被匿名内部类访问的局部变量必须使用final修饰:

37)Lambda表达式的类型也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法和私有方法,但只能包含一个抽象方法

38)Lambda表达式与内部类的联系与区别:

联系:1.两者都可以访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量)。

           2.Lambda表达式创建的对象和匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。

区别:1.匿名内部类可以为任意接口创建实例——不管该接口包含多少个抽象方法,只要匿名内部类实现所有抽象方法即可;但Lambda表达式只能为函数式接口创建实例。

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

           3.匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。

39)枚举类与普通类的区别:

       1.枚举类可以实现一个或多个接口,使用enum定义的枚举类型默认继承java.lang.Enum类,而不是默认继承Object类。因           此枚举类不能显式继承其他父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口。

       2.使用enum定义,非抽象的枚举类型默认使用final修饰,因此枚举类不能派生子类。

       3.枚举类的构造器只能用private修饰。如果省略访问控制符,默认使用private;如果强制指定访问控制符,只能指定private

       4.枚举类的所有实例必须在枚举类第一行显式列出,否则这个枚举类型永远不能产生实例。列出这些实例时系统会自动添加public static final修饰。

40)一旦为枚举类型显式定义了带参数的构造器,列出枚举值时就必须传入对应参数。如下:

41)实现接口的枚举类:

42)关于枚举类型,注意体会如下代码:

public enum Operation
{
	PLUS
	{
		public double eval(double x , double y)
		{
			return x + y;
		}
	},
	MINUS
	{
		public double eval(double x , double y)
		{
			return x - y;
		}
	},
	TIMES
	{
		public double eval(double x , double y)
		{
			return x * y;
		}
	},
	DIVIDE
	{
		public double eval(double x , double y)
		{
			return x / y;
		}
	};
	// 为枚举类定义一个抽象方法
	// 这个抽象方法由不同的枚举值提供不同的实现
	public abstract double eval(double x, double y);
	public static void main(String[] args)
	{
		System.out.println(Operation.PLUS.eval(3, 4));
		System.out.println(Operation.MINUS.eval(5, 4));
		System.out.println(Operation.TIMES.eval(5, 4));
		System.out.println(Operation.DIVIDE.eval(5, 4));
	}
}

二:易混淆出错的难点汇总

1)包装类的实例与数值类型的值进行比较时,直接取出包装类实例所包装的数值进行比较。如下图所示

      两个包装类的实例进行比较时,只有两个包装类的引用指向同一个对象时才会返回true。

      如果采用new构造器来创建Integer对象,则每次返回全新的Integer对象;如果采用valueOf()方法来创建Integer对象,则会缓存该方法创建的对象(不过只缓存-128~127之间的数)。如下示例代码:

public class IntegerCacheTest
{
	public static void main(String[] args)
	{
		// 生成新的Integer对象
		Integer in1 = new Integer(6);
		// 生成新的Integer对象,并缓存该对象
		Integer in2 = Integer.valueOf(6);
		// 直接从缓存中取出Ineger对象
		Integer in3 = Integer.valueOf(6);
		System.out.println(in1 == in2); // 输出false
		System.out.println(in2 == in3); // 输出true
		// 由于Integer只缓存-128~127之间的值,
		// 因此200对应的Integer对象没有被缓存。
		Integer in4 = Integer.valueOf(200);
		Integer in5 = Integer.valueOf(200);
		System.out.println(in4 == in5); //输出false
	}
}

      两个由自动装箱生成的Integer实例进行比较时,会出现如下特殊情况:

      这与如下Interger类的源代码设计有关,从下面源码可知系统将-128~127之间的整数自动装箱成Integer实例,并放入了名为cache的数组中缓存了起来,所以当-128~127的整数自动装箱成Integer类型时,实际上总指向已缓存的对应数组元素。但超过这个范围的整数自动装箱成Integer类型时,则系统总是重新创建一个实例。因此会出现上图所示结果

2)单例类。要求1:该类所有构造器均用private修饰。

                    要求2:提供一个public static方法用于作为该类的访问点,用于创建该类的对象。

                    要求3:提供一个static修饰的成员变量来保存曾经创建的对象,用于保证只创建一个对象。

注意体会如下程序:

3)可执行宏替换的final变量,需满足如下三个条件:

      1.使用final修饰符修饰

      2.在定义该final变量时制定了初始值。

      3.该初始值可以在编译时就被确定下来。

     看如下程序

      对于这个程序来说,变量a其实根本不存在,当程序执行System.out.println(a);代码时,实际转化为执行System.out.println(5);

      除了上面那种为final变量赋值时赋直接量的情况外,如果被赋的表达式只是基本的算术表达式或字符串连接运算,没有访问普通变量,调用方法,java编译器同样会将这种final变量当成宏变量处理。如下程序中,粗体字代码定义了4个final变量,程序为这4个变量赋初始值指定的初始值要么是算术表达式,要么是字符串连接运算。即使字符串连接运算中包含隐式类型转换,编译器依旧可以在编译时就确定a,b,str,book的值,因为他们都是宏变量。这段代码输出为true,false。为了加深对宏替换的理解,继续看如下程序:

上面程序中第一行粗体,由于编译时就可以确定s2的值,所以系统会让s2直接指向常量池中的“疯狂Java”字符串。

上面程序第二行粗体,由于s3的值由str1和str2进行连接运算后得到,而str1和str2是两个普通变量,编译时不会执行宏替换,因此输出false。若str1和str2是final变量则输出true。

4)对象与垃圾回收。

      对象在内存中的状态:

一个对象可以被一个方法的局部变量引用,也可以被其它类的类变量引用,或被其他对象的实例变量引用。当某个对象被其他类的类变量引用时,只有该类被销毁后,该对象才会进入可恢复状态;当某个A对象被其他对象的实例变量引用时,只有当该对象被销毁后,某个A对象才会进入可恢复状态。

    强制垃圾回收的方法:System.gc(); 和 Runtime.getRuntime().gc();

5)abstract与final永远不能同时使用;abstract和static不能同时修饰方法,可以同时修饰内部类;abstract和private不能同时修饰方法,可以同时修饰内部类。private和final可以同时修饰方法,但没有什么意义,因为private修饰的方法不可能被子类重写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值