Java SE —— (继承,接口,内部类)

继承(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();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值