继承(extends)
修饰符 | 同类 | 同包子类 | 同包非子类 | 非同包非子类 |
---|---|---|---|---|
private | ✔ | |||
(default) | ✔ | ✔ | ||
protected | ✔ | ✔ | ✔ | |
public | ✔ | ✔ | ✔ | ✔ |
子类构造方法中使用super()语句调用父类构造方法,用super关键字调用父类成员方法。 super只能用在构造方法中第一句。
-
重写(覆盖):
- 在子类中将父类成员方法的名称保留,重写成员方法的实现内容,更改成员方法的存储权限,或修改成员方法的返回值类型。重写父类方法时,修改方法的权限只 能从小范围到大范围改变。重写的返回值类型必须是父类中同一方法返回值类型的子类。
-
重构:
- 只改变成员方法的实现内容。子类与父类的成员方法返回值,方法名称,参数类型及个数保持不变。
-
重载:
- 在同一个类中允许同时存在一个以上的同名方法,但这些方法的参数
个数
或顺序
或类型
不同。
- 在同一个类中允许同时存在一个以上的同名方法,但这些方法的参数
继承机制:
- 创建一个子类对象,将包含一个父类子对象,这个对象与父类创建的对象是一样的。区别在于后者来自外部,而前者来自子对象的内部。
实例化子对象
- 实例化子对象时,父类对象也相应被实例化,即在子类的构造方法中自动调用父类的无参构造方法(有参构造方法只能依赖super关键字显示调用父类构造方法)。
调用顺序
- 顶级父类,次一级父类,最后是子类。即先实例化父类对象,然后实例化子类对象。故在子类构造方法访问父类构造方法之前,父类已经完成实例化操作。
阻止继承
- final类和方法 —— 声明类为final,则此类不允许扩展,类中方法自动转成final(子类不能覆盖此方法),但不包括域。
- 如果使用finalize()方法对对象进行清理,须确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收时,对象的所有部分都能被正常终止。
Object类
在java中,所有类都直接或间接继承了java.lang.Object类。任何类都可以重写Object类中的非final修饰方法。Object 类中的getClass()
,notify()
,notifyAll()
,wait()/wait(有参)
方法不能被重写,均被定义为final类型。
Java中 “相等” 判定相关方法:
-
1、== 符号相关判断机制
-
2、判断两个实例对象的引用是否指向内存中同一个实例对象,
obj1.equals(obj2);
-
3、判断实例对象是否为某个类、接口或其子类、子接口的实例对象,
class.isInstance(obj);
ArrayList<Object> objects = new ArrayList<>();
System.out.println(List.class.isInstance(objects)); //true
- 4、判断实例对象是否为某个类、接口的实例,
obj instanceof class
List list = new ArrayList();
if (list instanceof List) {
ArrayList arrayList = (ArrayList) list;
System.out.println("true" + " " + arrayList); //true []
}
- 5、判断一个类是否为另一个类本身或其子类、子接口,
class1.isAssignableFrom(class2)
if (List.class.isAssignableFrom(ArrayList.class)) {
System.out.println("true"); //true
}
对象类型的转换
(1)向上转型(upcasting)
把子类对象直接赋给父类引用,不用强制转型。向上转型只能引用父类对象的属性,要引用子类对象属性,则要写getter函数。
Father father = new Children();
向上转型的作用:减少重复代码,父类为参数,调用时用子类作为参数,就是利用了向上转型。这样使代码变得简洁。体现了JAVA的抽象编程思想。
(2)向下转型(downcasting)
把指向子类对象的父类引用赋给子类引用,要强制转型。
Father father = new Children(); Children children = (Children)father;
执行向下转型之前需使用instanceof
判断父类对象是否为子类对象的实例。使用instanceof
操作符判断一个类是否实现了某个接口,或一个实例对象是否属于一个类。
Father father = new Children();
if(father instanceof Children){
Children children = (Children) father;
...
}
多态
多态就是同一个接口,使用不同的实例而执行不同操作。
多态的优点
- 消除类型之间的耦合关系,可替换性,可扩充性,接口性,灵活性,简化性。
多态存在的三个必要条件(重点)
- 继承
- 重写
- 父类引用指向子类对象
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。【实现多态:父类引用指向子类对象后,用该父类引用调用子类重写的方法】
抽象类和接口
(1)抽象类
对象可以通过类来描绘,但并不是所有的类都可以用来描绘对象。故,一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类,可以使代码的重用。
- 抽象类主要用来进行类型隐藏,通过构造出一个固定描述抽象行为的方法,而这个方法能够有任意个可能的具体实现方式。
- 还可以用来拓展对象的行为功能,通过派生类可以确定不同对象的不同行为。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
抽象方法:
• 声明但却未被实现的方法,用abstract关键字修饰
抽象类要点:
- 有抽象方法的类必须定义成抽象类;
- 抽象类不能实例化(只有抽象类的非抽象子类可以创建对象);
- 抽象类只能被继承;
- 抽象类可以包含 字段,方法(抽象/具体方法),构造方法,静态代码块,但是构造方法不能用new来实例,只能用来被子类调用;
- 抽象方法可以有 public、protected 和 (default) 修饰符;
- 在抽象类中,类方法(用 static 修饰的方法)不能声明为抽象方法;
- 任何子类必须重写父类的所有抽象方法,或者声明自身为抽象类;
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
(2)接口:
接口是比抽象类还抽象的抽象类。更专业的实现:规范和具体实现的分离。
定义接口的详细说明:
- 访问修饰符:只能是public 或默认;
- 接口名:和类名采用相同的命名机制;
- extends:可以多继承;
- 常量:接口中的属性只能是常量,总是 public static final 修饰。不写也是;
- 方法:接口中的方法只能是 public abstract 。省略也是。
接口要点:
- 接口不能创建实例,但是可用于声明引用变量类型;(例:Map等)
- 类实现接口必须实现接口中的所有方法,并且只能是public 的;
- JDK 1.7 ,接口中只能包含静态常量,抽象方法;
- JDK 1.8 ,接口中可以有静态方法,默认方法(default)。
内部类
一个事物的内部包含另外一个事物,例如:身体与心脏的关系
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
- 内部类可以对同一包中其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
定义类时的访问修饰符规则:
-
外部类:public / (default)
-
成员内部类:public / protected / (default) / private
-
局部内部类:无
(1)成员内部类 (内用外,随意访问;外用内,使用内部类对象访问)
修饰符 class 外部类名 {
修饰符 class 外部类名 {
//...
}
//...
}
- 间接方式:在外部类的方法中,使用内部类,main只是调用外部类的方法
- 直接方式:外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
public class InnerClassDemo {
public static void main(String[] args) {
/**
* 间接方式
*/
Outer outer = new Outer();
outer.show();
/**
* 直接方式
*/
Outer.Inner inner = new Outer().new Inner();
inner.methodInner();
}
}
a) 非静态内部类
- i. 非静态内部类必须寄存在一个外部类对象里。非静态内部类对象单独属于外部类的某个对象。
- ii. 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
- iii. 非静态内部类不能有静态方法、静态属性和静态初始化块。
- iv. 外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
- v. 成员变量访问要点:
- 1. 内部类里方法的局部变量:变量名。
- 2. 内部类属性:this.变量名。
- 3. 外部类属性:外部类名.this.变量名。
public class Outer {
private int num = 10;
private double val = 12.0;
public class Inner {
private int num = 20;
public void methodInner() {
int num = 30;
System.out.println("内部类局部变量:" + num); // 30
System.out.println("内部类同名成员变量:" + this.num); // 20
System.out.println("外部类同名成员变量:" + Outer.this.num); // 10
System.out.println("外部类非同名成员变量:" + val); //12.0 内部类访问外部类,随意访问
}
}
/**
* 外部类访问内部类:使用内部类对象访问
* @return
*/
public void show() {
Inner inner = new Inner();
inner.methodInner();
}
}
b) 静态内部类
使用要点:
- 1. 当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。
- 2. 静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:“静态内部类.名字”的方式访问静态内部类的静态成员,通过 new 静态内部类()访问静态内部类的实例。
class Outer {
//相当于外部类的一个静态成员
static class Inner {
private int val = 15;
public int getVal() {
return val;
}
}
}
public class StaticInnerDemo {
public static void main(String[] args) {
//通过 new 外部类名.内部类名() 来创建内部类对象
Outer.Inner inner = new Outer.Inner();
System.out.println(inner.getVal());
}
}
(2)局部内部类
类定义在方法内部,只有当前所属的方法才能使用它,出了该方法就会失效。
1)局部内部类(包含匿名内部类)
修饰符 class 外部类名 {
修饰符 返回值类型 外部类方法名(参数列表) {
class 局部内部类名 {
//...
}
}
}
局部内部类访问所在方法的局部变量,则这个局部变量必须是有效的final变量(即没有在其他地方进行修改)
从JDK 8开始,只要局部变量事实不变,final关键字可省略
- 原因:new 出来的对象在堆中,局部变量存在于方法域中,在栈内存里,方法运行结束会立刻出栈,局部变量立刻消失,new出来的对象在堆中持续存在,直到垃圾回收消失。
public class Outer {
public void methodOuter() {
int num = 20; //方法所在的局部变量,为final值
class MyInner {
int num_1 = 30;
//num = 10; //错误写法
public void methodInner() {
//num = 10; //错误写法
System.out.println(num); //20
System.out.println(this.num_1); //30
}
}
//num = 10; //错误写法
MyInner myInner = new MyInner();
myInner.methodInner();
}
}
2)匿名内部类
若接口的实现类(或父类的子类)只需要使用唯一一次,则省略该类的定义,改用匿名内部类。
接口名 对象名 = new 接口名() {
//覆盖重写所有抽象方法
};
注意事项:
-
匿名内部类在【创建对象】的时候只能使用唯一一次
-
匿名内部类省略了【实现类/子类名称】,匿名对象省略了【对象名称】
-
匿名内部类没有访问修饰符
-
匿名内部类没有构造方法
-
匿名对象在【调用方法】的时候只能调用唯一一次
public interface MyInterface {
void method_1();
void method_2();
}
public class AnonymityInnerClassDemo {
public static void main(String[] args) {
MyInterface myInterface = new MyInterface() {
@Override
public void method_1() {
System.out.println("匿名内部类实现了方法method_1!");
}
@Override
public void method_2() {
System.out.println("匿名内部类实现了方法method_2!");
}
};
myInterface.method_1();
myInterface.method_2();
//使用了匿名内部类,省略了对象名,也是匿名对象
//缺点:匿名对象无法调用第二个方法,故需在创建一个匿名内部类的匿名对象来调用第二个方法
new MyInterface() {
@Override
public void method_1() {
System.out.println("匿名内部类实现了方法method_1!");
}
@Override
public void method_2() {
System.out.println("匿名内部类实现了方法method_2!");
}
}.method_1();
new MyInterface() {
@Override
public void method_1() {
System.out.println("匿名内部类实现了方法method_1!");
}
@Override
public void method_2() {
System.out.println("匿名内部类实现了方法method_2!");
}
}.method_2();
}
}
———》》》
package com.jdk8.test;
public class MyTest {
public final int value = 4;
public void doIt()
{
int value = 6;
int val = 8;
Runnable r = new Runnable(){
public final int value = 5;
public void run(){
int value = 10;
System.out.println(value); // 10
System.out.println(this.value); // 5
System.out.println(val); // 8 【为非同名变量】
System.out.println(MyTest.this.value); // 4
}
};
r.run();
System.out.println(value); // 6
System.out.println(val); // 8
}
public static void main(String...args)
{
MyTest myTest = new MyTest();
myTest.doIt();
}
}