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

一: 简单零碎的知识汇总

1)java中定义类的简单语法如下:

      其中修饰符可以是public,abstract, final或者完全省略这三个修饰符

2)同一个类中定义顺序没有任何影响,各成员之间可以相互调用,但是static修饰的成员不能直接访问没有static修饰的成员

3)如果没有为类编写构造器,则使用默认构造器。

4)定义成员变量的语法格式如下:[修饰符] 类型 成员变量名 [ = 默认值]

      其中修饰符可以省略,也可以是public,protected,private,static,final

5)定义方法的语法格式如下

      其中修饰符可以省略,也可以是public,protected,private,static,final,abstract。其中final和abstact只能出现一个

6)命名规则:

     类名:一个或多个单词连缀而成,每个单词首字母大写,单词之间不要任何分隔符。

     成员变量名和方法名:一个或多个单词连缀而成,第一个单词首字母小写,其余单词首字母大写,单词之间不要任何分隔符

7)static修饰的成员变量和方法称作类变量,类方法不使用static修饰的普通方法,成员变量则属于该类的单个实例,也称作

      例变量,实例方法

8)定义构造器的语法格式如下:

      其中修饰符可以省略,也可以是public,protected,private其中之一。

      构造器不能定义返回值类型,且构造器名必须与类名相同。一旦定义返回值类型(包括使用void),构造器就成了方法。

9)static定义的变量和方法,既可以通过类名来调用,也可以通过实例来调用;没有使用static的变量和方法只能通过实例调用。

10)this关键字总是指向调用该方法的对象,根据this出现的位置不同,this作为对象的默认引用有两种情形。

       1.构造器中引用该构造器正在初始化的对象

       2.在方法中引用调用该方法的对象

11)java允许一个成员直接调用另外一个成员,可以省略this前缀。如果调用static修饰的成员时省略了前面的主调,那么默认使

       用该类作为主调;如果调用没有static修饰的成员时省略了前面的主调,那么默认使用this作为主调。

       下面程序演示了静态方法直接访问非静态方法时引发的错误:

12)Java中方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数值的副本(复制品)传入方法内。

13)定义方法时,在最后一个形参的类型后增加三个点(...),则表明该形参可以接受多个参数值。下面程序定义了一个形参个数          可变的方法

14)递归一定要向已知方向递归,

       例1.已知,f(0)=1,f(1)=4,f(n+2)=2*f(n+1)+f(n)。求f(10)。此时递归语句如下:

       例2.已知f(20)=1,f(21)=4,f(n+2)=2*f(n+1)+f(n)。求f(10)。此时递归语句如下:

15)方法重载的要求是两同一不同,即同一个类,同一个方法名,不同参数列表。方法的其他部分,如返回值类型,修饰符等,

        与方法重载没有关系。

16)

17)成员变量无需显式初始化,默认初始化时的赋值规则与数组动态初始化时数组元素的赋值规则完全相同。

        局部变量除形参外,都必须显式初始化。

18)Java允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在方法中

        引用被覆盖的成员变量,则可使用this(对于实例变量)或类名(对于类变量)作为调用者来限定访问成员变量。

19)

20)1.子类继承父类的所有成员变量和方法,但是不继承构造器。

        2.Java语言不支持多继承,即每个类只能有一个直接父类,但可以有无限多个间接父类

        3.java.lang.Object所有类的父类,因此所有的Java对象都可以调用java.lang.Object类所定义的实例方法。

21)方法的重写要遵循两同两小一大规则。两同指方法名相同,形参列表相同;两小指子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明中抛出的异常类更小或相等;一大指的是子类方法的访问权限应必父类更大或相等。

22)重写中,覆盖方法和被覆盖方法必须都是类方法或都是实例方法,不能一个是类方法,一个是实例方法,否则会编译出错。

23)如果父类的方法用private修饰,则该方法不可在子类中访问,也不能被继承(自然也不能被重写,所以可以说是默认final类型)。

24)Overload主要发生在同一个类的同名方法间,Override主要发生在子父类的同名方法间。但子类也可以重载父类的同名方法。

25)super关键字用于调用当前对象的父类对象的实例变量和方法。

26)this和super都不能出现在static修饰的方法中。

27)当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,也会为它从父类继承的所有实例变量分配内存,即使两个实例变量名相同。子类中定义的与父类中同名的实例变量并不会完全覆盖父类中定义的实例变量,它只是简单的隐藏了父类中的实例变量。

28)初始化块的修饰符只能是static。

29)类实例化的顺序:

父类静态成员和静态代码块 -> 子类静态成员和静态代码块 -> 父类非静态成员和非静态代码块 -> 父类构造方法 -> 子类非静态成员和非静态代码块 -> 子类构造方法

30)初始化块是一个假象,使用 Javac 命令编译 Java 类后,该 Java 类中的初始化块会消失,初始化块中代码会被"还原"到每个构造器中,且位于构造器所有代码的前面。

31)构造方法可以处于public、protected、private和默认四种访问级别之一,没有返回值。 

        但不同于普通方法,构造方法(器)不能是abstract, static, final, native, strictfp, 或者synchronized的。原因如下:

  1. 构造器不是通过继承得到的,所以没有必要把它声明为final的。
  2. 同理,一个抽象的构造器将永远不会被实现。(所以也不能声明为abstract的)
  3. 构造器总是关联一个对象而被调用,所以把它声明为static是没有意义的。
  4. 没有实际的需要把构造器定义成同步的,因为它将会在构造的时候锁住该对象,直到所有的构造器完成它们的工作,这个构造的过程对其它线程来说,通常是不可访问的。 (synchronized)
  5. 本地化的方法情况特别复杂,所以JVM调用起来非常麻烦,需要考虑很多种情况,没有native关键字的情况下,JVM实现起来比较容易

32)一旦为类提供新的构造器,默认构造器将不再存在。

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

1)   Person p=new Person();

         下图显示了Person对象在内存中存储示意图

        下图显示将Person对象赋给一个引用变量的示意图

2)为什么static修饰的静态成员不能直接访问非静态成员?

答:因为对于 static 修饰的方法 ,则可以使用类来直接调用该方法,如果在 static 修饰的方法中使用 this 关键字, 则这个关键

字就无法指向合适的对象 所以,static修饰的方法不能使用this引用,所以static修饰的方法不能访问不使用static修饰的普通成

员。java语法规定: static修饰的静态成员不能直接访问非静态成员。

3)方法中的参数进行值传递时,什么情况下参数本身会发生变化?

答:如下代码段,参数本身值不变。

原因如下:

如下代码段中,参数值本身发生了变化:

原因如下

4)成员变量初始化和内存中的运行机制:

      对于如下代码

      其中name为实例变量,eyeNum为类变量

      每一步的存储示意图如下:

       

       

       系统会在第一次使用Person类时加载这个类,并初始化这个类。在类的准备阶段,系统将会为该类的类变量分配内存空间,并指定默认初始值。从图5.10可以看出,当Person类初始化完成后,系统将在堆内存中为Person分配一块内存区(当Person类初始化完成后,系统会为Person类创建一个类对象),在这块内存区里包含了保存eyeNum类变量的内存,并默认初始值为0。

5)关于构造器

6)如果构造器B完全包含了构造器A,如下图5.16所示。对于这种完全包含的情况,则可在B中调用方法A。但构造器不能直接被调用,构造器必须使用new进行调用,但一旦使用new就创建了一个新的对象。为了在构造器B中调用构造器A中的初始化代码,又不会重新创建一个Java对象,可以使用this关键字调用相应的构造器,此时B中用this调用A的构造器代码必须出现在B构造器第一行,如下图代码所示。

        这种调用方法的理由和优点如下:

7)子类中定义的与父类中同名的实例变量并不会完全覆盖父类中定义的实例变量,它只是简单的隐藏了父类中的实例变量。

所以会出现如下特殊情形:

如上代码执行代码Derived d = new Derived();后内存中的存储示意图如下图所示:

此时访问哪个tag则由d的类型决定

8)在子类构造器中可以用super(参数...)调用父类构造器,此时super必须出现在子类构造器第一行

     不论是否使用super调用来执行父类构造器的初始化代码,子类构造器总会调用父类构造器一次。此时情况分如下三种:

            1.子类构造器第一行使用super显式调用父类构造器,系统根据super调用传入的实参列表调用父类构造器。

            2.子类构造器第一行使用this显式调用本类中重载的构造器。系统将根据this调用里传入的实参调用本类中另一个构造                   器。执行另一个构造器时,继续检测第一行是否使用this和super显式调用构造器或隐式调用父类无参构造器,依次                       类推直至父类构造器被调用一次。

             3.子类构造器第一行既未使用this显式调用本类中重载的构造器,也未使用super显式调用父类构造器,则系统在执行子                  类构造器前默认调用一次父类无参构造器。

      因此创建任何java对象最先执行的总是java.lang.Object类的构造器。

9)在编译时类型和运行时类型不同的情况下,多态会发生,务必通过如下代码来体会:

class BaseClass
{
	public int book = 6;
	public void base()
	{
		System.out.println("父类的普通方法");
	}
	public void test()
	{
		System.out.println("父类的被覆盖的方法");
	}
}
public class SubClass extends BaseClass
{
	//重新定义一个book实例变量隐藏父类的book实例变量
	public String book = "轻量级Java EE企业应用实战";
	public void test()
	{
		System.out.println("子类的覆盖父类的方法");
	}
	public void sub()
	{
		System.out.println("子类的普通方法");
	}
	public static void main(String[] args)
	{
		// 下面编译时类型和运行时类型完全一样,因此不存在多态
		BaseClass bc = new BaseClass();
		// 输出 6
		System.out.println(bc.book);
		// 下面两次调用将执行BaseClass的方法
		bc.base();
		bc.test();
		// 下面编译时类型和运行时类型完全一样,因此不存在多态
		SubClass sc = new SubClass();
		// 输出"轻量级Java EE企业应用实战"
		System.out.println(sc.book);
		// 下面调用将执行从父类继承到的base()方法
		sc.base();
		// 下面调用将执行从当前类的test()方法
		sc.test();
		// 下面编译时类型和运行时类型不一样,多态发生
		BaseClass ploymophicBc = new SubClass();
		// 输出6 —— 表明访问的是父类对象的实例变量
		System.out.println(ploymophicBc.book);
		// 下面调用将执行从父类继承到的base()方法
		ploymophicBc.base();
		// 下面调用将执行从当前类的test()方法
		ploymophicBc.test();
		// 因为ploymophicBc的编译类型是BaseClass,
		// BaseClass类没有提供sub方法,所以下面代码编译时会出现错误。
		// ploymophicBc.sub();
	}
}

       Java允许将一个子类对象直接赋给父类引用变量,无需任何类型转化,向上转型由系统自动完成。此时引用变量编译时为父类类型,运行时为子类类型。因为子类方法重写父类方法,所以方法具有多态性,多态发生时方法行为总是表现出子类方法的行为特征;因为子类同名实例变量仅仅隐藏父类实例变量,所以实例变量不具有多态性,引用类型编译时是什么类型,就访问该类型的实例变量。注意:如上代码中:BaseClass ploymophicBc=new SubClass();ploymophicBc编译时为BaseClass类型,运行时为SubClass类型。

10)关于引用变量的强制类型转换,被转换类型和转换类型之间必须有子父类关系,且强制类型转换一般为向下类型转换,即将父类转换成子类;子类转换为父类为向上类型转化,一般由系统自动完成。

        值得注意的是,即使编译时强制类型转换通过,因为多态的发生,运行时仍可能抛出ClassCastException异常。

        注意理解如下代码:

public class ConversionTest
{
	public static void main(String[] args)
	{
		double d = 13.4;
		long l = (long)d;
		System.out.println(l);
		int in = 5;
		// 试图把一个数值类型的变量转换为boolean类型,下面代码编译出错
		// 编译时候会提示: 不可转换的类型
		// boolean b = (boolean)in;
		Object obj = "Hello";
		// obj变量的编译类型为Object,Object与String存在继承关系,可以强制类型转换
		// 而且obj变量实际上类型是String类型,所以运行时也可通过
		String objStr = (String)obj;
		System.out.println(objStr);
		// 定义一个objPri变量,编译类型为Object,实际类型为Integer
		Object objPri = Integer.valueOf(5);
		// objPri变量的编译时类型为Object,objPri的运行时类型为Integer,Object与Integer存在继承关系
		// 可以强制类型转换,而objPri变量实际上类型是Integer类型,
		// 所以下面代码运行时引发ClassCastException异常
		String str = (String)objPri;
	}
}

11)instanceof运算符的前一个操作数一般是一个引用变量,后一个操作数一般是一个类(可以是接口)。

        instanceof运算符用于判断前面的对象是否是后面的类,或者其子类,或者其实现类的实例。

        instanceof运算符前一个操作数编译时类型要么与后面的类相同,要么与其具有父子类关系,否则编译错误。

        注意理解如下代码:

public class InstanceofTest
{
	public static void main(String[] args)
	{
		// 声明hello时使用Object类,则hello的编译类型是Object,
		// Object是所有类的父类, 但hello变量的实际类型是String
		Object hello = "Hello";
		// String与Object类存在继承关系,可以进行instanceof运算。返回true。
		System.out.println("字符串是否是Object类的实例:"
			+ (hello instanceof Object));
		System.out.println("字符串是否是String类的实例:"
			+ (hello instanceof String)); // 返回true。
		// Math与Object类存在继承关系,可以进行instanceof运算。返回false。
		System.out.println("字符串是否是Math类的实例:"
			+ (hello instanceof Math));
		// String实现了Comparable接口,所以返回true。
		System.out.println("字符串是否是Comparable接口的实例:"
			+ (hello instanceof Comparable));
		String a = "Hello";
//		// String类与Math类没有继承关系,所以下面代码编译无法通过
//		System.out.println("字符串是否是Math类的实例:"
//			+ (a instanceof Math));
	}
}

     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值