8.1继承的基础
继承是面向对象的基本特征之一
8.1.1 继承的概念
继承是一种面向对象的编程概念,它允许一个类(称为子类)从另一个类(称为父类或超类)继承属性和方法。子类可以使用继承来扩展或修改父类的属性和方法,但同时也可以保留父类的特性。
使用关键字"extends"来实现继承。子类声明时,在类名后面使用“extends”关键字,然后跟上父类的名称。子类将继承父类的所有public和protected成员变量和方法,但不会继承私有成员变量和方法。
8.1.2继承的语法
-
使用关键字extend(扩展)实现
public class Dog extends Animal{ }
子类可以从父类继承属性和部分方法,自己再增加新的属性和方法。通过继承可以重用父类的方法和属性,减少代码重复编写,便于维护、代码扩展。
8.1.3 对继承的说明
(1)不能继承的资源:私有方法,属性.构造方法,没有访问权限的;即不能继承那些不能访问的方法。在子类中不能访问到的那些方法,无法继承的。
理论上子类会继承父类的全部成员变量,但是子类不能访问父类的私有成员变量,如果子类与父类在不同包中,子类也不能访问父类中具有默认访问权限的成员变量。
(2)只允许单继承;只能继承一个;接口除外.
(3)可以定义相同的成员变量,会隐藏/覆盖同名成员变量.
(4)可以定义与父类中同名的成员方法,这时子类中的方法重写了父类中的同名方法。
8.1.4子类的构造方法
-
按照继承顺序从父类到子类调用构造函数;例如: 父类里面的无参构造函数写上"我是父类的无参",其子类的无参里面写上"我是子类的无参", 在new 子类();这个时候,就会同时显示出"我是父类的无参""我是子类的无参".
-
在子类的构造方法中,一定会首先调用父类的构造方法。
-
子类的每个构造方法都会隐式的调用父类的无参数构造方法,如果想调用父类的其他构造方法,必须使用super(参数列表)来显式调用。
-
如果父类没有无参的构造方法,或者想调用父类的有参构造方法,则在子类的构造方法中必须显式使用super(xxx)调用父类有参构造方法。这时super(xxx)必须是子类中的第一条语句。
-
编写类时,通常需要提供无参数构造方法。
-
在父类中定义有参数的构造方法,负责初始化父类的成员变量。
在子类的构造方法中,先调用父类的构造方法完成从父类继承来的那些成员变量,然后初始化子类中特有的成员变量。
-
父类中定义了一个有参数的构造方法,系统就不会再为父类提供默认的构造方法。这时,在子类的构造方法中,必须使用super(xxx)显示调用父类的有参构造方法。
8.1.5 创建多级继承层次
public GrandFather( ){
}
public Father( ) extends GrandFather{
}
public Son( ) extends Father{
}
8.1.6 方法重写介绍
当子类从父类中继承来的方法不能满足需要时,子类可以重写该方法,重写方法要求方法名与参数列表都相同。
超类引用变量可以引用子类对象
SuperA sa; //声明超类的变量
A a = new A(); //创建子类对象
sa = a; //将子类对象赋给引用对象
sa = new A(); //创建一个新的子类对象,赋给超类引用变量
可以访问哪些成员是由引用变量的类型决定的,而不是由所引用的对象类型决定的。
8.1.7 对象的转型
子类转父类 小转大可以自动转
父类转子类 大转小需要强转前提是:父类引用确实指向了正确的子类对象
8.2 super关键字
关键字super用于调用/访问从父类中继承来的实例变量和方法。
super有两种一般用法。第一种用于调用超类的构造方法。第二种用于访问超类中被子类的某个成员隐藏的成员。
8.2.1 使用super()调用父类的构造方法
-
子类中使用super()调用父类的构造方法时,必须第一条语句;
-
本类中使用this()调用重载的构造方法时,必须是第一条
-
this()和super()不能同时使用
8.2.2 使用super访问父类中被子类隐藏的成员变量
隐藏的可以使用关键字super; 父类的属性被子类继承,如果子类又添加了名称相同的属性,则子类有两个相同名称的属性,如果父类型对象调用属性,就是父类的,如果是子类型对象调用就是子类的属性。
8.3 Object
Object是所有其他类的父类.可以引用数组.
方 法 | 用 途 |
---|---|
Object clone() | 创建一个和将要复制的对象完全相同的新对象。 |
boolean equals(Object object) | 确定一个对象是否和另外一个对象相等 |
void finalize() | 在回收不再使用的对象前调用 |
Class<?> getClass() | 在运行时获取对象的类 |
int hashCode() | 返回与调用对象相关联的散列值 |
void notify() | 恢复执行在调用对象上等待的某个线程 |
void notifyAll() | 恢复执行在调用对象上等待的所有线程 |
String toString() | 返回一个描述对象的字符串 |
void wait()void wait(long milliseconds)void wait (ling milliseconds,int nanoseconds) | 等待另一个线程的执行 |
boolean equals(Object object)
子类转object都可以;
equals:表示与其他对象是否相等.
int hashCode()
Student st = new Student(); Student st1=st; System.out.println(st.hashCode()); System.out.println(st1.hashCode());
输出结果:
460141958 460141958
Student st = new Student(); Student st1=new Student(); System.out.println(st.hashCode()); System.out.println(st1.hashCode());
输出结果:
460141958 1163157884
String toString()
返回一个描述对象的字符串
8.3.2对象相等性比较
Object类中的equals()方法实现等价于“==”运算符,比较相等,如果实现对象的内容相等比较,自己的类必须重写equals方法。
8.3.3 Object类的常用方法
Cat cat = new Cat(); cat.name="加菲猫"; Cat cat1 = new Cat(); cat1.name="加菲猫"; System.out.println(cat.equals(cat1));
结果:false//比较全部
Cat cat = new Cat(); cat.name="加菲猫"; Cat cat1 = new Cat(); cat1.name="加菲猫"; System.out.println(cat.name.equals(cat1.name));
结果:true
l equals(Object obj)方法
比较对象相等 Object类的实现是 等价于 ==
相等的含义:两个引用是否指向同一个对象。
l toString()方法
直接打印对象时,默认调用对象的toString()方法
Object类的toString方法输出格式:
getClass().getName() + '@' + Integer.toHexString(hashCode())
自己的类要重写toString()
l rotected Object clone()
克隆对象的方法 被克隆的对象的类必须实现Cloneable接口
l finalize()方法 //终结方法
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
l HashCode()方法
返回该对象的哈希码值
当我们重写equals()方法,判断两个对象相等时,最好也同时重写hascode()方法,让相同对象的哈希码值也相同
8.3.4、**"=="和equals方法究竟有什么区别?**
==比较栈,equals()比较堆
== 解读
对于基本类型和引用类型 == 的作用效果是不同的,如下所示:
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同
(单独把一个东西说清楚,然后再说清楚另一个,这样,它们的区别自然就出来了,混在一起说,则很难说清楚)
==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。
如果一个变量指向的数据是对象类型的,那么,这时候涉及了两块内存,对象本身占用一块内存(堆内存),变量也占用一块内存,例如Objet obj = new Object();变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的内存中的数值是否相等,这时候就需要用==操作符进行比较。
equals方法是用于比较两个独立对象的内容是否相同,就好比去比较两个人的长相是否相同,它比较的两个对象是独立的。例如,对于下面的代码:
String a=new String("foo");
String b=new String("foo");
两条new语句创建了两个对象,然后用a,b这两个变量分别指向了其中一个对象,这是两个不同的对象,它们的首地址是不同的,即a和b中存储的数值是不相同的,所以,表达式a==b将返回false,而这两个对象中的内容是相同的,所以,表达式a.equals(b)将返回true。
在实际开发中,我们经常要比较传递进行来的字符串内容是否等,例如,String input = …;input.equals(“quit”),许多人稍不注意就使用==进行比较了,这是错误的,随便从网上找几个项目实战的教学视频看看,里面就有大量这样的错误。记住,字符串的比较基本上都是使用equals方法。
如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码如下:
boolean equals(Object o){
return this==o;
}
这说明,如果一个类没有自己定义equals方法,它默认的equals方法(从Object 类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一对象,这时候使用equals和使用==会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相同,那么你必须覆盖equals方法,由你自己写代码来决定在什么情况即可认为两个对象的内容是相同的。
8.4 final
8.4.1 final修饰变量、方法、类
-
如果final修饰变量,变量就是常量,常量不可修改,定义时必须初始化
-
如果final修饰方法,方法就不能被子类重写
-
如果final修饰类,类就不能再被扩展,不能再有子类。Java类库中的String、Math就是final类。
-
对象不能再重新赋值
8.4.2 引用类型的常量
如果常量是基本数据类型,不可以再修改。
如果常量是引用类型,不能再将其他对象赋给该引用,但可以使用该引用改变对象内部的属性。