父类与子类关系:继承关系
-
继承通过extends 关键字来声明: 子类 extends 父类
-
继承的作用 : 继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系, 提高了软件的可重用性和可扩展性,提高了代码复用性.
- 继承中this与super的使用
子类中属性名跟父类属性名相同,则子类的属性隐藏了父类的属性 , 此时想引用父类变量则需要用关键字 super 来引用
一个类中的局部变量名与该类的成员变量名相同,此时想引用成员变量需要关键字this来引用
this 代表本类对象引用, super 代表父类对象引用
this. 方法\属性 调用本类方法或访问本类属性super. 方法\属性 调用父类方法或访问父类属性
this(); 调用本类构造函数;super() 调用父类构造函数.
this()或super()调用构造函数必须作为构造函数的第一条语句
一个构造函数不能同时调用多个本来构造函数也不能同时调用多个父类构造函数
一个构造函数不能同时调用本类构造函数和父类构造函数,即构造函数中不能同时使用this和super来调用子父类构造函数
子类构函数默认都会隐式调用的父类构函数,即执行语句super();
每个类如果没有显式定义构造函数,则系统自动添加空参数空方法体的构造函数,当定义了含参的构造函数后系统不再添加
空参空方法体的构造函数,如果要使用空参数的构造方法则必须显式的定义,否则调用空参数的构造函数时会出错,
由于每个子类构造方法在没有通过super显式指明调用父类哪个构造函数时,系统自动调用super(),即调用父类空参数的
构造函数,所以如果父类因为定义了含参的构造函数后,不含参的构造函数就不存在了,要使用空参数构造函数必须显式
定义 ,如果没有显式定义那么子类构造函数必须用super(参数)
表明调用父类含参构造函数,当显式的调用了含参的父类构造函数之后 就不会再调用不含参的父类构造函数
- 继承中方法的重写
重写又叫覆盖 ,重写是相对于方法而言的,是指具有子父类的继承关系的方法的重新实现过程子类的属性名跟父类的同名时只是隐藏,在子类中直接访问该名的属性时相对于用this.属性名
来访问,由于子类隐藏了父类的属性,所以this.属性名访问的是子类的属性,要访问被隐藏的父类
属性可以通过super.属性名 来访问
静态成员的"覆盖"也是隐藏,起不到覆盖的作用 , 要访问父类被隐藏的静态方法或属性通过父类名访问
只有非静态方法才能称为覆盖
覆盖发生在继承关系的子父类的非静态成员方法之间和实现关系的接口与实现类的方法之间
覆盖的特点
1)方法名称 参数列表 返回类型子类方法名称 参数列表 返回类型必须与父类方法的名称,参数列表和返回类型一致
2)权限修饰子类方法不能缩小父类访问权限,子类重写父类必须保证子类权限大于等于父类权限
为什么不能缩小访问权限?因为如果没权限限制将与多态机制发生冲突
3)static修饰静态只能覆盖静态,父类的非静态方法不能被子类覆盖为静态方法
4)final修饰父类被final修饰的方法都不能被子类覆盖 ,被final修饰的父类方法被子类继承后子类可以
直接对该方法进行重载
5)throws异常声明子类方法不能抛出比父类方法更多的异常
子类方法抛出的异常必须和父类方法抛出的异常相同或子类方法抛出的异常是父类方法
抛出的异常类的子类父类被覆盖方法没有抛出异常--->子类覆盖方法不能抛出异常
父类被覆盖方法抛出异常--->子类覆盖方法不抛出异常
或抛出跟父类被覆盖方法相同的异常
或抛出跟父类被覆盖方法抛出的异常的子类异常
6) abstract 修饰父类的非抽象方法可以被覆盖为抽象方法
- 覆盖与重载区别:
相同点:
都要求方法同名
都可以用于抽象方法与非抽象方法之间
不同点:
方法覆盖要求参数列表必须一致,而方法重载要求参数列表必须不一致
方法覆盖要求返回类型必须一致,而方法重载没有限制
方法覆盖发生在子父类方法之间,方法重载发生在同一个类的所有方法包括从父类继承来的方法
方法覆盖对方法的权限和抛出异常有特殊要求,而方法重载对此没限制
父类的一个方法只能被子类覆盖一次,一个方法所在的类中可以被重载多次
- final 修饰符
final 表示“这是无法改变的” 可以修饰 非抽象类 非抽象类的方法和变量 ,不能修饰构造函数
final修饰抽象类或方法没有意义,因为抽象类不能创建对象,必须有其子类把所有抽象的方法实现了之后才能创建,被final修饰后就无法被继承
final修饰类(非抽象) :不允许继承、没有子类,方法不允许复写
final修饰变量(成员变量,局部变量,静态变量):不允许改变属性值,被修饰的变量是一个常量只能赋值一次,赋值后值不变
final修饰方法:不允许修改函数,被修饰的方法不能被子类重写,但可以被继承和重载
- 继承的功能:
1 , 子类直接使用父类功能
2,子类重写父类功能,改变功能的具体实现
3,子类重写父类功能并扩展内容
4,子类扩展父类没有的功能,加入了新内容和功能
继承使子类共享了父类的属性和方法,还能扩展自己的属性和方法,提高软件重用性和扩展性
创建子类对象时,首先通过默认的supper()去调用父类构造函数,然后调用子类构造函数
子类的所有构造函数默认调用父类构造函数supper();且该语句在第一行
若要调用父类带参构造函数则需要显式调用supper(参数)且放在第一行;
若父类没有定义空参数的构造函数,则必须显式给父类定义带参构造函数,
子类的每个构造函数也得显式调用父类哪个构造函数
本类中的构造函数要调用本类构造函数则通过this()或this(参数)来调用
但同一个构造函数不能同时调用本类和父类构造函数任何类的构造函数都会调用父类构造函数,若没写则调用父类的无参构造函数,若父类定义了含参的构造函数,
子类需要显式写出要调用父类哪个含参构造函数,一个构造函数不能同时调用多个别的构造函数
无论调用本类构造函数还是父类构造函数,调用别的构造函数的语句必须放在第一行
1.如果父类中没有构造函数,即使用默认的构造函数,那子类的构造函数会自动调用父类的构造函数
class Father
{
private int a,b;
void show()
{
System.out.println(a);
}
}
class Son extends Father
{
private int c,d;
Son(int c, int d)
{
this.c = c;
this.d = d;
}
}
public class ConstructionTest
{
public static void main(String args[])
{
Son s = new Son(2, 3);
s.show();
}
}
输出结果0,说明子类的构造函数自动调用父类的无参构造函数,初始化父类的成员为02,如果定义了有参构造函数,则不会有默认无参构造函数,
此时(没有用super调用父类有参构造函数的情况下)子类在调用父类的空参数构造函数的时候会出错
抽象类
- 抽象就是抽取功能,只抽取功能定义,不抽取功能主体 ,格式如下 :
abstract void 抽象方法名();
- 抽象类就是用abstract关键词修饰的类 ,abstract既可以修饰类也可以修饰方法
- 抽象类的特点及与接口的异同:
1,抽象方法一定在抽象类中,抽象类可以有非抽象方法,可以一个抽象方法都没有(目的就是防止创建本类对象)
2,抽象方法和抽象类都必须被abstract修饰 抽象类的抽象方法不能被final或 private修饰
3,抽象类不可以用new创建对象,即便没有抽象方法
4,抽象类中所有的抽象方法要被使用必须由子类复写添加方法主体后,才能建立子类对象并调用,
即子类继承抽象父类后必须重写所有的父类抽象的方法,若没有全部复写,则该子类还是抽象类,故不能创建子类对象
5,抽象类可以一个抽象方法都没有,这样的类也不能被实例化
6, 抽象类中的抽象方法的权限修饰可以是public,protected(默认类型,虽然 eclipse下不报错,但应该也不行)
但接口中的抽象方法只能是public类型的,并且算定义没写默认也为public abstract类型。
7,抽象类中可以包含静态方法,接口中不能包含静态方法
8,抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。9 , 抽象类可以继承其他类也可以实现其他接口
10 抽象类中全部方法都为抽象方法时并不等于接口,因为类是被用来继承(extends)的而接口则被用来实现(implements)的
11 抽象类可以定义构造方法
-
为提高代码复用性
将对象共享的变量抽取为--静态变量
将对象共享的方法抽取为--静态方法
将类共同的功能抽取为--抽象类/接口将构造方法共同的初始化内容抽取为--构造代码块
将类的初始化抽取--静态代码块
- 抽象类与一般类和接口的区别
- 当抽象类跟普通类一样所有的方法都是非抽象时,他们还是有区别的 , 就是抽象类是被abstract修饰的 , 所以不能实例化
- 当抽象类跟接口一样所有的方法都是抽象的时候,他们还是有区别的 , 就是抽象类还是类可以被其他类继承并实现其中的
抽象方法,接口可以被其他类实现,实现其中的方法
- 抽象类可以跟一般的类一样有构造方法。无参,有参的构造方法都行。
- 可以声明抽象类的对象引用。但是不可以实例化对象。必须依靠子类来实例化。
- 抽象类中的抽象方法的权限修饰可以是public,protected
接口中的抽象方法只能是public类型的,并且算定义没写默认也为public abstract类型。- 抽象类中可以包含静态方法,接口中不能包含静态方法
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,
但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
接口
变量默认修饰符:public static final
方法默认修饰符:public abstract void (其中void必须写)
只要是接口 无论有没有给变量或方法加以上修饰符,系统会自动添加
class 定义类
interface 定义接口接口与接口之间存在继承关系且可以多继承
类与类之间是继承关系 extends 且只能单继承
类与接口之间是实现关系 implements 可以多实现
接口不能创建对象,因为抽象方法的存在
子类只有把接口的所有抽象方法都实现之后才能实例化
子类全部实现了接口的抽象方法之后才能实例化,否则还是抽象类
支持多实现,因为不同接口的相同抽象方法必须在子类中实现之后才能使用,不会产生冲突
扩展的功能放在接口中,基本的功能放在父类中
java只支持单继承, 因为多继承容易带来安全隐患:当多个父类定义相同功能但各种功能的实现过程时,不确定运行哪个
java支持多层继承 ----子>>父类>>父父类抽象类中定义抽象方法,目的是不让创建该类对象,强迫子类复写该功能
抽象类与接口的相同点
(1)都不能创建实例
(2)都必须被子类把他们的所有抽象方法都实现了之后才能实例化
(3)
(1)
抽象类和接口的区别
(1)接口可以多实现,抽象类只能单继承
(2)接口只能声明功能,不能实现功能,抽象类可以只声明功能或一部分功能只声明一部分功能已经实现或所有功能全已实现
(3)接口的字段定义默认为:public static final, 抽象类字段默认是"friendly"包内可见
java只能直接继承于一个类,它的类型只能限定于其基类或基类的基类,但java可以多实现,可以有多个
接口类型,就像一个人,他继承自脊椎动物,而脊椎动物有继承动物,张三是个人,也是一个脊椎动物,但他
可以继承多个接口,如有驾驶证后他是驾驶证持有者类型,他有英语六级证书,那么他是六级证持有者类型
多态
1,多态的体现
父类或接口的引用指向了自己的子类对象 Person p = new Person();
父类或接口的引用可以可以接收自己的子类对象或实现类对象
父类或接口有众多的子类或实现类,父类或接口的引用可以接收所有继承它的子类或实现它的实现类
父类跟子类的继承关系是属性和功能的共性提取, 接口与实现类的实现关系提供了功能的扩展性(方便后期扩展)
Animal a=new Cat(); //类型提升,向上转型
a.eat;
Cat c = (Cat)a; //向下转型,将父类的引用强制转换成子类
不能将父类对象转成子类类型 , 如:
Animal b = new Animal();
Cat c = (Cat)b;//error
2,多态的前提
-
必须是有继承关系或实现关系
-
存在方法覆盖(子类覆盖父类方法)或方法实现(抽象类中抽象方法被子类实现或接口中抽象方法被实现类实现)
父类的引用指向自己的子类对象 --继承
接口型引用指向自己的子类对象 --实现
3,多态的好处
-
多态的出现大大提高了程序扩展性,,前期定义的代码可以使用后期的内容。
-
一个接口或父类的引用可以指向多种实现类型或子类类型,引用指向谁就可以调用谁的覆盖的方法或调用谁实现的方法
4,多态的弊端
-
提高了扩展性 , 但是只能使用父类的引用访问父类中的成员,
-
不能访问父类引用所指向的子类的特有的非继承来的属性和方法
-
不能访问接口引用所指向的实现类的特有的非来自接口的属性和非实现接口的方法
5, 多态中成员的特点
- 注意子父类成员变量同名不属于覆盖,只能说是隐藏
-
动态绑定 静态绑定
绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,
绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定。静态绑定:
在程序执行前方法已经被绑定,针对java简单的可以理解为程序编译期的绑定;这里特别说明一点,
java当中的方法只有final,static,private和构造方法是前期绑定(静态绑定)的。动态绑定:
后期绑定:在运行时根据具体对象的类型进行绑定。
-
多态中成员函数的特点:
Fu f = new Zi(); //向上转型
编译看左 运行看右
在编译时,参阅引用变量所属类中有没所调用的方法,如果有编译通过 , 如果没有编译不通过
在运行时,参考引用变量所指向的对象的类是否有调用的方法,有则运行子类方法,没有则运行父类方法;
- 多态中的类的成员变量和接口的常量的特点:
无论编译还是运行,都参考引用变量所属的类或接口 , 当子类定义了跟父类相同的属性时,父类引用只能访问父类的属性
-
多态中静态方法的特点:
编译和运行都参考左边,即引用变量所属的类 , 因为静态方法就是用 父类名.方法 访问的 , 不依赖于具体对象
父类的static方法子类可以继承,也可以覆盖
下面举个实例:
public class ToUp{
public static void main(String[] args){
Fu f = new Zi();//向上转型
System.out.println(f.s);// 父类引用f只能访问是父类的属性s
//System.out.println(f.c);// 父类引用f不能访问子类的属性c
f.show();//父类引用f调用的子类覆盖方法show
Zi z =(Zi)f;//向下转型
System.out.println(z.s);//z访问的子类属性,不能访问父类被隐藏的属性
z.show();//z调用的子类show方法
}
}
class Fu{
int i = 1 ;
String s = "父类属性";
public void show(){
System.out.println("父类show方法 " );
}
}
class Zi extends Fu{
int c = 2;
public String s ="子类属性";
public void show(){
System.out.println("子类show方法");
}
}
运行结果为:
父类属性
子类show方法
子类属性
子类show方法
1)Fu f = new Zi();向上转型,f只能访问父类的属性和父类未被覆盖的方法和所指向的子类覆盖父类的方法
f.show(); f调用的子类show方法
父类引用指向子类对象,只能访问父类属性,调用子类覆盖的方法
2)Zi z =(Zi)f; 向下转型 , z.s z访问的子类属性 , z.show() z调用的子类show方法
向下转型后 既可以访问子类属性也可以访问父类属性,既可以调用子类特有的方法(包括覆盖的)也可以调用继承自父类的方法
Object 类
- Object类是所有对象的直接或间接父类,通常称为上帝类,定义了所有对象都具备的功能
- 比较功能:equals 只要是对象都具有可比性,比较两对象是否相同,默认比较对象地址值
Object 类的 equals 方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x 和 y,
当且仅当 x 和 y 引用同一个对象时,此方法才返回 true(x == y 具有值 true)。
Object 类的 equals 方法默认比较对象地址值,我们通常要复写该方法因为可能我们要比较的对象
是否相同判断的依据是他们的逻辑或者说内容上是否相等,而且该方法被复写时hashcode方法也同时被复写
以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
HashMap对象是根据其Key的hashCode来获取对应的Value。在重写父类的equals方法时,也重写hashcode方法,
使相等的两个对象获取的HashCode也相等,这样当此对象做Map类中的Key时,两个equals为true的对象其获取的value都是同一个,比较符合实际。
-
获取哈希值:hashCode() 是一个本地方法 它返回对象的哈希码值, 哈希码值是 int 类型整数
hashCode 的常规协定是:
->在 Java 应用程序执行期间(一次运行期内),在对同一对象多次调用 hashCode 方法时,返回的必须相同的整数,前提是将
对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
->如果根据 equals(Object) 方法,两个对象x和y是相等的,那么x.hashCode()==y.hashCode() 必为true
->如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定
生成不同的整数结果。
- 获取类对象: getClass() 返回对象的类型对象 getClass().getName()获取类型对象名称
- toString 返回对象的字符串表示 Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一
个实例)、at 标记符“@ ”和此对象哈希码的无符号十六进制表示组成。
换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())
一般建议子类会复写该方法,用来获取更易懂的信息
- 线程同步有关的操作:
- wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
- notify() 唤醒在此对象监视器上等待的单个线程。
- notifyAll() 唤醒在此对象监视器上等待的所有线程。
这三个方法用于协调多个线程对共享数据的存取,所以必须在synchronized语句块内使用。
synchronized 关键字用于保护共享数据,阻止其他线程对共享数据的存取,但是这样程序的流程就很不灵活了,如何才能在当前线程还没退出synchronized数据块时让其他线程也有机会访问共享数据呢?
此时就用这三个方法来灵活控制。
wait()方法使当前线程暂停执行并释放对象锁,让其他线程可以进入synchronized数据块,
当前线程被放入对象等待池中。当其他线程调用notify()方法后,将从对象的等待池中移走一个任意的线程并放到锁标志等待池中,
只有锁标志等待池中线程能够获取锁标志;如果锁标志等待池中没有线程,则notify()不起作用。
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
- Class<T> 类实例表示当前正在运行的java程序的类或接口
枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,
所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型
(boolean、byte、char、short、int、long、float 和 double)和关键字
void 也表示为 Class 对象。