3-4 面向对象高级
继承格式
课堂笔记
-
继承机制的引入使得类之间有了等级划分。
- 继承是子类继承父类的行为和特征。子类还可以拥有专属于自己属性和方法。
- 使用继承避免了重复定义某些类的属性和方法,代码复用率会提高。
- 举例:学生类可以继承人类,因为学生也属于人类。
-
继承格式:
class Students extends Person{ //类描述 }
知识拓展
- JAVA中只有单继承,多重继承,没有多继承。
- C++语言中有多继承:一个子类可以有多个父类,这里有个问题,当多个父类有相同名称的方法或属性,在继承多个父类时就会产生冲突。诸如此类问题导致在Java中直接去掉了这个特性。
- 多重继承:Java中若存在需要继承多个类时,可以使用多重继承,即作为孙子类去继承父类和爷类,同样可以实现多继承的效果。
子类实例化内存分析
课堂笔记
- 当创建子类对象时,查找其是否有父类,若有父类,则会先创建其父类,然后创建子类对象,并且在子类中创建super引用,其指向父类的内存空间。
- 当调用子类的方法或属性时,发现子类没有对应的资源,则接着去super中去查找。
- 所谓继承,子类只是拥有了指向其父类的引用,并不是在同一块内存中。另外,还涉及到父类中的权限访问问题,子类只能操作父类中的public和protected部分。
super关键字
课堂笔记
- 通过super去操作父类的构造方法:
- 若父类中使用了自定义的传参构造方法,即无法使用默认的构造方法。在子类中若不调用父类的构造方法则无法继承父类,因为内存创建顺序是先创建父类再创建子类的对象,而父类又无法使用系统默认的构造方法,导致无法创建。
- 解决方法:在子类中调用父类的传参构造方法,使用
super(x,y)
或者在父类中描述一个无参构造方法,这样就不用在子类中显式地调用父类的构造方法了。 - 使用super操作父类的构造方法时,必须在子类构造方法的第一行先调用。
- 通过super去操作父类的属性和方法,仍然需要考虑到访问权限问题。
知识拓展
- super关键字是用来操作父类的,而this关键字是用来操作本身的。
- super和this均在使用构造方法时,两者不会同时出现,也不能同时出现。
- 在构造方法中调用this来调起其他构造方法,也就是说当前的构造方法只是中转,那么也就意味着必须把this构造方法写在第一行,先调用真正的构造方法来构造这个对象。
- 而super出现在构造方法中,也是必须写在第一行,要先创建出父类才有子类。
- super和this在一个类中都出现的情况下,要谨慎使用,避免发生"互锁调用"。
重写与重载的区别
课堂笔记
- 重写发生在继承的过程中,如子类中有和父类相同的方法(完全一样),这时以子类的方法为准,即在子类中将该方法进行了重写。
- 重写的方法的权限必须不小于父类的权限。
- 父类的成员方法只能被他的子类重写。
- 声明static和private的方法不能重写:都不能继承,当然就不能重写。
- 重载和重写完全不是一个东西:
- 重载(Overload)本质是,只是名称相同,而参数列表不同。
- 重载发生在一个类中。
- 重写的本质是,完全一样的两个方法,包含返回值、名称、参数列表。
- 重写发生在父子类中。
- 重写对访问权限是有要求的,重写时不得不低于原来的权限。
- 重写对异常的范围也是要求的,不能抛出新的异常,可以减少异常。
final 关键字
课堂笔记
- 与变量的概念相对的是常量,而final关键字就是用来修饰变量为常量的。
- 可以修饰属性、变量,无法再次对其进行赋值。其中对final修饰属性必须在声明的同时进行赋值,而对局部变量则可以先声明后赋值,但均只能赋值一次。
- 全局常量:
public static final a =0;
变量a可以通过类名在全局中任意位置访问到。 - final修饰的类是不能被继承的,同理,final修饰的方法也是不能被重写的。
知识拓展
- final修饰类同与C/C++中使用
const
修饰常量。而const
是Java预留关键字,用于后期扩展用,用法跟final相似,不常用。
抽象类
课堂笔记
-
非抽象的类:描述清楚的类,属性和方法都描述完整。
-
抽象类:无法一次性描述清楚的类,其存在抽象方法,其方法必须写在抽象类或者接口中。
abstract class Person{ public abstract void func() ; // 抽象方法,只声明而未实现 }
-
抽象类无法直接被实例化,也就是说其必须使用继承机制才能进行使用。且必须在继承的非抽象类的子类中实现重写。
知识拓展
-
抽象类能否使用final声明?权限能否为public或protected
不能,因为final属修饰的类是不能有子类的 ,权限也必须是public和protected(不写时为public),子类才能继承。 而抽象类必须有子类才有意义,所以不能。 -
抽象类能否有构造方法?
能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的
构造方法(默认是无参的),之后再调用子类自己的构造方法。 -
随记:一个java文件中最好只写一个类,且为public类。@override用来检查是否为重写。
接口
课堂笔记
-
接口与抽象类最大的区别在于,其内方法全部为抽象方法,属性为全局常量。
- 抽象方法缺省为public abstract,可以省略。
- 接口中的全局常量缺省为public static final,可以省略。
-
接口声明格式:
interface 接口名称{ 全局常量; 抽象方法; } /* 子类实现继承抽象类,且实现多实现接口 */ class 子类 extends 父类 implement 父接口1,父接口2...{ } /* 接口多继承 */ interface C extends A,B{ }
-
接口的继承:接口都是抽象部分,不存在具体实现,所以允许多继承,不会产生继承时的冲突问题。
-
如果一个接口要想使用,必须依靠子类。 子类(如果不是抽象类的话)要实现接口中的所有抽象方法
-
接口与抽象类的区别:
- 1、抽象类要被子类继承,接口要被类实现。
- 2、接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
- 3、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
- 4、抽象类使用继承来使用, 无法多继承。 接口使用实现来使用, 可以多实现。
- 5、抽象类中可以包含static方法 ,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
- 6、接口不能有构造方法,但是抽象类可以有。
知识拓展
- 面向接口的编程思想:
- 定义和实现进行分离,只实现规范和约束,而实际操作需要在子类中实现。
- 很明显降低了程序耦合性,易于程序扩展,利于程序维护。
- 在写代码时,建议先写接口。
- 接口有子、父接口的概念,但不是类,也就是说其不存在额外的堆内存空间,也不需要构造方法。
多态
课堂笔记
- 对象的多态,多种表现形式。子类就是父类的一种形态 ,对象多态性就从此而来。
- 重载: 一个类中方法的多态性体现。
- 重写: 子父类中方法的多态性体现。
- 多态的使用:对象的类型转换
- 类似于基本数据类型的转换:
- 向上转型:将子类实例变为父类实例,格式:父类 父类对象 = 子类实例 ;
- 向下转型:将父类实例变为子类实例,格式:子类 子类对象 = (子类)父类实例 ;
- 特别注意:向下转型时,默认有一个前提条件:若其父类实例是经过向上转型的的父类实例,则转型指向的子类实例必须和当前将要向下转型的子类相同,否则不能转换。
- 多态的实际应用:
- 将某个接口作为一个方法的参数传入。
- 在其他类中实现这个接口。
- 传入不同的子类(该类中实现了接口的方法)来达到调用不同重写的方法。
instanceof
关键字
- 作用:判断某个对象是否是指定类的实例,则可以使用
instanceof
关键字 - 格式:实例化对象
instanceof
类 //此操作返回boolean
类型的数据 - 注意:使用该关键字检测子类实例属于父类是正确的,而父类实例是不属于子类的。
Object 类
课堂笔记
- 其存在的最大意义在于可以接收任意的引用数据类型(多态性),进而处理不同类型的对象逻辑。
- Object 类是基类,也是所有类的祖宗,默认无继承声明的类的父类都是Object类。
- Object类提供了一些方法,供所有类使用,但更多的是供所有类进行重写,以便实现自己独特的业务需求,这也体现了Java语言的多态性。
- 重写Object类方法的举例:
toString
:用于打印对象的字符串表示形式。未重写情况下,打印出的是my.test.Car@7d6f77cc
,即对象完整类名@地址。a.equals(b)
:其中a为本类的对象,而b为Object类的对象。- 在未重写的情况下:使用的是
==
进行引用的比较,即必须是同一个内存中的数据才相等。 - 子类可以重写该方法:注意在重写的方法中如果要用到b对象的属性,需要在使用前向下转型。
- 在重写时需要满足五个特性:自反性、对称性、传递性、一致性和非空性。
- 在未重写的情况下:使用的是
知识拓展
- Object的作用类似于C/C++语言中的
void*
参数,解决因输入数据的类型不确定而带来的问题。
内部类(了解)
课堂笔记
- 后续不怎么使用,由于使用起来不太方便,且易使用其他方式实现同样的效果。
- 成员内部类:
- 定义位于另一个类的内部。
- 内部类无条件访问外部类所有资源,本质原因:在访问时,外部资源已经在内存中分配。
- 内外部同名资源,采用就近原则。若在内部要使用同名的外部类资源,则需要使用
外部类名.this
。
- 局部内部类:
- 定义在一个方法或者作用域里的类。
- 没有太大的意义,其使用场景:
- 当需要传入一个接口实现类时,而该实现只使用一次,则可以现做一个局部内部类用来实现该接口,并做一个对象传入即可。
Frame类
中有一个用于监控窗口的接口addWindowsListener(L)
,而需要传入的L
需要现做一个接口的实现并做一个对象L
传入。
- 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及
static修饰符的。没有意义。
- 匿名内部类:
- 属于局部内部类的一种,但该类没有名字,直接new出一个无名的接口实现类对象并直接赋值给接口。只能使用一次。
- 必须继承一个类或者实现一个接口,但是两者不能兼得。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
- 只能访问final型的局部变量。注意:即使在外部定义的变量没有声明final,只要匿名内部类里使用到了这个变量,则会默认给该变量限定为final。
- 静态内部类:
- 成员内部类前加
static
修饰,就是静态内部类的格式。 - 内存分配中看:静态内部类已经分配,此时外部类还没有创建,因此在静态内部类中不能访问外部类的动态变量和方法。
- 成员内部类前加
包装类
课堂笔记
-
引入的目的是:解决基本数据类型需要符合
一切皆对象
的原则。 -
Java中的八种基本数据类型并不是引用类型,所以为了解决这个问题,引入了包装类。
-
除了
int
的包装类Integer
以及char
的包装类Character
外,其他基本数据类型的包装类均为其对应写法的首字母大写的形式。 -
包装类的父类:除
Character
和Boollean
的父类是Number
类,爷类是Object类,其他包装类的父类均为Object
类。Number
类还提供了一些方法,例如可以将字符串转换成对应的数字类型,举例:parseInt
、parseFloat
和parseBoolean
等。 -
装箱和拆箱操作:
-
将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。
-
将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作。
-
Number类提供的拆箱方法:
xxx xxxValue()
返回对应的基本数据类型xxx
。 -
装箱操作:
- 在JDK1.5,Java新增了自动装箱和自动拆箱,而且可以直接通过包装类进行运算操作。
在JDK1.4之前 ,如果要想装箱,直接使用各个包装类的构造方法即可,例如: int temp = 10 ; // 基本数据类型 Integer x = new Integer(temp) ; // 将基本数据类型变为包装类 在JDK1.5,Java新增了自动装箱和自动拆箱,而且可以直接通过包装类进行四则运算和自增自建操作。例 如: Float f = 10.3f ; // 自动装箱 float x = f ; // 自动拆箱 System.out.println(f * f) ; // 直接利用包装类完成 System.out.println(x * x) ; // 利用基本数据类型
-
可变参数
课堂笔记
-
语法格式:
/* 注意由于arg是不定长参数 如果有其他参数 应放在其前面 */ void func(int aaa,int... arg){ /* 使用时以数组的方式访问 且长度为其length属性值 */ for(int i = 0;i<arg.length;i++){ System.out.println(arg[i]) ; } }
知识拓展
- 前面说过Java多态性体现之一的
Object类
,其功能效果和C/C++中的void *
类似。 - 这里的可变参数,在C/C++中也有对应的特性,使用方法相比Java而言要复杂一些,格式也不同。
递归
课堂笔记
- 简而言之,就是自己调用自己。
- 栈溢出问题:程序调用另一个程序时是需要在栈中新开辟一块内存空间用来保存上下文现场数据的,直到调用的程序返回后,恢复上下文,并继续执行下面的代码。
- 如果使用递归方法,没有对递归的深度做一定限制,当递归深度到达一定值时,就会因为栈溢出而崩溃。