【JavaSE】_7.抽象类和接口

目录

1. 抽象类

1.1 抽象类概念

1.2 抽象类语法及特性

1.3 抽象类作用

2. 接口

2.1 接口概念

2.2 接口语法

2.3 接口特性

2.4  实现多个接口

2.5 接口间的继承

2.6 接口使用实例:给对象数组排序

2.7  Clonable 接口和深拷贝

2.7.1 Clonable接口实现浅拷贝

2.7.2 深拷贝

2.8 抽象类和接口的区别

2.9 Object 类

2.10 equals 方法

3. 内部类

3.1 内部类的分类

3.1.1  静态内部类

3.1.2  实例内部类

3.1.3 局部内部类

 3.1.4 匿名内部类


1. 抽象类

1.1 抽象类概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但并不是所有的类都是用来描绘对象的。

如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类;

1.2 抽象类语法及特性

抽象类使用abstract修饰,其特性如下:

1.抽象类不能直接实例化对象:

abstract class Shape{
    //define an abstract class
    public void draw(){
        System.out.println("Drawing a shape.");
    }
}
public class AbstractClasses {
    public static void main(String[] args) {
        Shape shape = new Shape();
    }
}

报错如下:

2. 抽象类内的方法可以不写具体内容,但为保证方法语法的正确性,需要加上abstract修饰: 

abstract class Shape{
    public abstract void draw();
}

注:① 抽象类的方法可以是非抽象的,但抽象方法所在的类必须是抽象类; 

② 抽象类内也可以不含有抽象方法;

3.  当一个普通类继承抽象类后,该类必须继承抽象父类中的抽象方法:

abstract class Shape{
    public abstract void draw();
}
class Rectangle extends Shape{

}
public class AbstractClasses {
    public static void main(String[] args) {

    }
}

报错如下:

 4. 抽象类因继承而存在,否则其内部的普通成员方法由于无法实例化而无法使用对象引用调用,

只有继承后通过子类使用super对其普通成员方法进行调用:

abstract class Shape{
    //define an abstract class
    public abstract void draw();
}
class Rectangle extends Shape{
    @Override
    public void draw(){
        System.out.println("Drawing a rectangle.");
    }
}
class Circle extends Shape{
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("Drawing a flower.");
    }
}
class Triangle extends Shape{
    @Override
    public void draw() {
        System.out.println("Drawing a triangle.");
    }
}
public class AbstractClasses {
    public static void drawMap(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        drawMap(new Rectangle());
        drawMap(new Circle());
        drawMap(new Flower());
        Shape shape = new Triangle();
        shape.draw();
    }
}

5. 抽象方法为了保证可以被重写,必须满足重写条件,即不可以被private、final、static修饰

6. 当一个子类没有重写抽象的父类方法,可以把当前子类变为abstract修饰;

1.3 抽象类作用

当某些场景下,需要满足某些功能实际不该由父类完成,而应该由子类完成,此时如果误用了父类,普通类不会报错,但是抽象类就会报错,其实就是多了一重编译器的检查;

预防出错在编写代码中非常常见,如final修饰类防止误继承等等,充分利用编译器的校验在实际开发中是非常有意义的;

2. 接口

2.1 接口概念

接口就是公共行为规范标准,在实现时只要满足标准就可以使用;

在java中,接口就是多个类的公共规范,是一种引用数据类型;

2.2 接口语法

接口的定义格式与定义类的格式基本相似,只需将class关键字更改为inteface即可:

public interface IShape{
 }

注:① 一般情况下都是在package下新建class或interface;

② 接口名一般都以大写字母I开头;

③ 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性;

2.3 接口特性

1. 接口类型是一种引用类型,但是不能直接new接口的对象:

interface IShape{
    public abstract void draw();
}

public class Interfaces {
    public static void main(String[] args) {
        IShape shape = new IShape();
    }
}

报错如下:

2. 接口当中的成员默认是public static final 修饰的:

interface IShape{
    public String name1 = "triangle";
    public static  String name2 = "rectangle";
    public static final String name3 = "pentagon";
}

name1、name2、name3虽然显式书写的修饰符不相同,但其实都是public static final修饰的都是静态常量,这也决定了接口当中不允许存在构造方法

3. 在接口中只允许有不存在具体实现内容的抽象方法,且默认是public abstract 修饰的:

interface IShape{
    public void fun(){
        System.out.println("Drawing a shape.");
    }
}
public class Interfaces {
    public static void main(String[] args) {
    }
}

报错如下:

但从JDK8开始允许存在默认的default方法:在有具体实现内容的成员方法前加上default修饰:

interface IShape{
    default public void fun(){
        System.out.println("Drawing a shape.");
    }
}

 4.  接口需要被类实现时,需要使用implements关键字,并且类似于类的继承,同样具有向上转型、动态绑定等等功能:

package Intefaces;
interface IShape{
    public String name1 = "triangle";
    public static  String name2 = "rectangle";
    public static final String name3 = "pentagon";
    public abstract void draw();
}
class Hexagon implements IShape{
    @Override
    public void draw() {
        System.out.println("Drawing a hexagon.");
    }
}
class Octagon implements IShape{
    @Override
    public void draw(){
        System.out.println("Drawing a octagon.");
    }
}
public class Interfaces {
    public static void drawMap(IShape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        IShape shape1 = new Octagon();
        IShape shape2 = new Hexagon();
        drawMap(shape1);
        drawMap(shape2);
    }
}

输出结果为:

5. 接口当中允许存在static修饰的方法,可以通过 接口.方法 的方式调用:

interface IShape{
    public static void func(){
        System.out.println("This is a shape.");
    }
}
public class Interfaces {
    public static void main(String[] args) {
        IShape.func();
    }
}

 输出结果为:

6. 重写接口方法时,不能使用默认的访问权限

(重写必须满足:子类方法不能比父类方法修饰权限更小,父类中为public,子类中不可为默认包访问权限)

interface IShape{
    public String name1 = "triangle";
    public static  String name2 = "rectangle";
    public static final String name3 = "pentagon";
    public abstract void draw();
}
class Hexagon implements IShape{
    @Override
    void draw() {
        System.out.println("Drawing a hexagon.");
    }
}

报错如下:

7. 接口中不能有静态代码块和构造方法;

8. 接口虽然不是类,但接口编译完成后字节码问价你的后缀格式也是.class;

9. 如果没有实现接口中的抽象方法,则类必须设置为抽象类;

10. jdk8中:接口还可以包含default方法;

2.4  实现多个接口

在java中不允许类的多继承,只允许单继承,故而每个类只有一个父类,但一个类可以实现多个接口;

以动物类、狗类、鱼类、鸟类、鸭类以及会跑、会游、会飞接口为例展示多个接口:

abstract class Animal{
    public String name;
    public Animal(String name){
        this.name = name;
    }
}
interface IRunning{
    public void run();
}
interface ISwimming{
    public void swim();
}
interface IFlying{
    public void fly();
}
class Dog extends Animal implements IRunning{
    //先继承再实现
    public Dog(String name){
        super(name);
    }
    public void run(){
        System.out.println(name+" is running.");
    }
}
class Fish extends Animal implements ISwimming{
    public Fish(String name){
        super(name);
    }
    public void swim(){
        System.out.println(name+" is swimming.");
    }
}
class Bird extends Animal implements IFlying{
    public Bird(String name){
        super(name);
    }
    public void fly(){
        System.out.println(name+" is flying.");
    }
}
class Duck extends Animal implements IRunning,ISwimming,IFlying{
    public Duck(String name){
        super(name);
    }
    @Override
    public void run() {
        System.out.println(name+" is running.");
    }
    @Override
    public void swim() {
        System.out.println(name+" is swimming.");
    }
    @Override
    public void fly() {
        System.out.println(name+" is flying.");
    }
}
class Robot implements IRunning{
    public void run(){
        System.out.println("The robot is running.");
    }
}
public class Demo1 {
    public static void swim(ISwimming iSwimming){
        iSwimming.swim();
    }
    public static void walk(IRunning iRunning){
        iRunning.run();
    }
    public static void main(String[] args) {
        walk(new Dog("Mike"));
        walk(new Duck("Mary"));
        swim(new Fish("Jake"));
        swim(new Duck("Karry"));
        walk(new Robot());
    }
}

输出结果为:

观察walk与swim方法,其参数为接口类型,即我们在传参时可以忽略类型关注行为,即是不是动物,机器人类拥有run行为就可以使用IRunning接口;

即:接口打破了类型的限制,只要具备了该接口的行为,就可以使用该接口;

2.5 接口间的继承

在java中类与类只能单继承,一个类可以实现多个接口,接口之间可以进行多继承;

interface A{
    void funA();
}
interface B{
    void funB();
}
interface CC extends A,B{
    void func();
}
class C implements CC{
    public void funA(){
    }
    public void funB(){
    }
    public void func(){
    }
}
public class Demo1{
    public static void main(String[] args) {
    }
}

(1)CC接口不仅具备func这个功能,还具备了A和B接口的funA和funB的功能;

(2)使用CC接口继承接口A和B而非CC类,这样可以免于在CC内部重写funA和funB方法

(3)extends关键字不仅可以理解为类之间的继承,也可以理解为接口的功能扩展;

2.6 接口使用实例:给对象数组排序

(1)试运行以下程序:

import java.util.Arrays;
class Students{
    public String name;
    public int age;
    public int score;
    public Students(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) {
        Students[] students = new Students[3];
        students[0] = new Students("Mike",19,90);
        students[1] = new Students("Mary",20,97);
        students[2] = new Students("Jake",18,92);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

 报错及分析如下:

 GoTo Comparable Declaration or Usages: 

Comparable interface 内部实现了compareTo方法,需要在使用该接口的类中重写该方法:

(2)以比较年龄和姓名为例:

比较年龄:

    public int compareTo(Students o){
        if(this.age>o.age){
            return 1;
        }
        else if(this.age<o.age){
            return -1;
        }
        else{
            return 0;
        }
    }

比较姓名:(String类已经实现了Comparable接口,可以直接使用)

函数实现为:

public int compareTo(Students o){
        if(this.name.compareTo(o.name)>0){
            return 1;
        }
        else if(this.name.compareTo(o.name)<0){
            return -1;
        }
        else{
            return 0;
        }
    }

调用语句为:

Students[] students = new Students[3];
students[0] = new Students("Mike",19,90);
students[1] = new Students("Mary",20,97);
System.out.println(students[0].compareTo(students[1]));

姓名对比逻辑为依次对比字母大小,输出结果分别为:-1,1;

(3)现将compareTo方法内部实现为年龄比较,此时再试运行排序:

import java.util.Arrays;
class Students implements Comparable<Students>{
    public int compareTo(Students o){
        if(this.age>o.age){
            return 1;
        }
        else if(this.age<o.age){
            return -1;
        }
        else{
            return 0;
        }
    }
    public String name;
    public int age;
    public int score;
    public Students(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) {
        Students[] students = new Students[3];
        students[0] = new Students("Mike",19,90);
        students[1] = new Students("Mary",20,97);
        students[2] = new Students("Jake",18,92);
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

或写为:

    public int compareTo(Students o) {
        return this.age-o.age;
    }

 此时输出结果为:

 (4)可以使用冒泡排序进行简化逻辑模拟实现:

 public static void sort(Comparable[] array){
        for(int i=0;i< array.length-1;i++){
            for(int j=0;j< array.length-i-1;j++){
                if(array[j].compareTo(array[j+1])>0){
                    Comparable tmp = array[j];
                    array[j]=array[j+1];
                    array[j+1]=tmp;
                }
            }
        }
    }

(5)Comparable比较法写定一种比较方法后就无法再更改,是非常不灵活的,也可以使用比较器Comparator进行排序:

Comparator内部实现了compare方法,实现该接口需要重写该方法:

 运行代码如下:

import java.util.Arrays;
import java.util.Comparator;

class Students {

    public String name;
    public int age;
    public int score;
    public Students(String name, int age, int score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public String toString() {
        return "Students{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }
}
class AgeComparator implements Comparator<Students> {
    @Override
    public int compare(Students o1, Students o2) {
        return o1.age-o2.age;
    }
}
class ScoreComparator implements  Comparator<Students>{
    @Override
    public int compare(Students o1, Students o2) {
        return o1.score-o2.score;
    }
}
class NameComparator implements Comparator<Students>{
    @Override
    public int compare(Students o1, Students o2) {
        return o1.name.compareTo(o2.name);
    }
}
}
public class Test1 {
    public static void main(String[] args) {
        AgeComparator ageComparator = new AgeComparator();
        ScoreComparator scoreComparator = new ScoreComparator();
        NameComparator nameComparator = new NameComparator();
        Students[] students = new Students[3];
        students[0] = new Students("Mike",19,90);
        students[1] = new Students("Mary",20,97);
        students[2] = new Students("Jake",18,92);
        Arrays.sort(students,ageComparator);
        System.out.println(Arrays.toString(students));
        Arrays.sort(students,scoreComparator);
        System.out.println(Arrays.toString(students));
        Arrays.sort(students,nameComparator);
        System.out.println(Arrays.toString(students));
    }

输出结果为:

 该方法可以实现多个比较方式共存,需要不同的排序方式调用对应的比较器即可;

一般建议类内使用Comparable方式实现其中一种排序方式,在类外使用比较器实现其他排序方式,这样可以避免显式调用比较器时的运行错误;

2.7  Clonable 接口和深拷贝

2.7.1 Clonable接口实现浅拷贝

① 类似于Clonable接口这样的空接口也称为标记接口,仅表示当前对象是可被克隆的:

需要使用该接口时,需要重写Object的克隆方法:

代码示例1: 自定义类型的拷贝:

class Student implements Cloneable{
    public String name;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}
public class Test1 {
    public static void main(String[] args)throws CloneNotSupportedException {
        Student student1 = new Student();
        student1.name = "Mike";
        Student student2 =(Student)student1.clone();
        // clone方法的返回类型是Object类型,用Student类型接收会发生向下转型
        // 向下转型需要强制类型转换
        System.out.println(student1);
        System.out.println(student2);
    }
}

输出结果为: 

代码示例2:

class Money{
    public double money = 12.25;
}
class Student implements Cloneable{
    public String name;
    public Money m = new Money(); //存储对象的地址
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

}
public class Test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Student student1 = new Student();
        Student student2 = (Student)student1.clone();
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
        System.out.println("--------------");
        student2.m.money = 9.9;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

输出结果为:

在修改克隆对象的内容时,原对象也发生了改变,这样的克隆方式称为浅拷贝:

浅拷贝底层内存分布如下:

克隆student2时并没有克隆student1对象中的m对象, 即student1和student2对象内部的m对象指向的是同一个money对象,

2.7.2 深拷贝

而修改克隆对象的内容不影响原对象内容的克隆方式称为深拷贝,需要实现深拷贝试想其内存分布应如下图所示:

 根据此底层逻辑图试实现代码:

class Money implements Cloneable{
//Money类实现克隆需要实现Clonable接口
    public double money = 12.25;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Student implements Cloneable{
    public String name;
    public Money m = new Money(); //存储对象的地址
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException { 
        //克隆student对象
        Student student = (Student)super.clone();
        
        //克隆money对象
        student.m = (Money)this.m.clone();
        return student;
    }

}
public class Test1 {
    public static void main(String[] args) throws CloneNotSupportedException{
        Student student1 = new Student();
        Student student2 = (Student)student1.clone();
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
        System.out.println("--------------");
        student2.m.money = 9.9;
        System.out.println(student1.m.money);
        System.out.println(student2.m.money);
    }
}

此时输出结果为:

 实现深拷贝需要将当前对象内的每个对象都实现克隆

2.8 抽象类和接口的区别

核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不包括普通方法,子类必须重写所有的抽象方法;

比如Animal类中的name属性,这个属性在所有子类中都是存在的,故而此处的Animal只能作为抽象类而不能作为接口:

class Animal{
    protected String name;

    public Animal(String name){
        this.name = name;
    }
}
No 区别 抽象类 接口
1 结构组成 普通类+抽象方法 抽象方法+全局常量
2 权限 各种权限 public
3 子类使用 使用extends关键字继承抽象类 使用implements关键字实现接口
4 关系 一个抽象类可以实现若干接口 接口不能继承抽象类,但是接口可以会用extends关键字继承多个父接口
5 子类限制 一个子类只能继承一个抽象类 一个子类可以实现多个接口

2.9 Object 类

Object是java默认提供的一个类,在java中除了Object类,所有的类都是存在继承关系的,默认会继承Object父类,即:所有类的对象都可以使用Object类进行接收:

class Person{

}
class Animal{

}
public class Test2 {
    public static void fun(Object obj){

    }
    public static void main(String[] args) {
        fun(new Person());
        fun(new Animal());
    }
}

 此时再联系toString方法:

class Animal{

}
public class Test2 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        System.out.println(animal);
    }
}

Ctrl点击查看:

 用Object类的对象x 接收了 Animal类实例化的对象animal,发生了向上转型;

再Ctrl点击查看:

valueOf方法内部调用了obj.toString方法,此时便是重写即调用子类toString,未重写即调用父类toString,发生动态绑定;

2.10 equals 方法

试运行以下代码:

class Freshman{
    public String name;

    @Override
    public String toString() {
        return "Freshman{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Test3{
    public static void main(String[] args) {
        Freshman freshman1 = new Freshman();
        freshman1.name = "Mike";
        System.out.println(freshman1);
        Freshman freshman2 = new Freshman();   //每次new会开辟新内存
        freshman2.name = "Mike";
        System.out.println(freshman1);
        System.out.println(freshman1 == freshman2); //内存分布是不同对象
        System.out.println(freshman1.equals(freshman2));
    }
}

 输出结果为:

 在Students类内部没有实现equals方法,Ctrl点击进入equals方法查看:

调用的是父类Object类的equals方法,该方法的实现逻辑仍与==相等,为比较对象引用是否相等,如果需要实现仅对比对象内容是否相等,需要在子类中重写Object类的equals方法:

class Freshman{
    public String name;

    @Override
    public String toString() {
        return "Freshman{" +
                "name='" + name + '\'' +
                '}';
    }

@Override
    public boolean equals(Object obj) {
        if(obj == null){     //判断obj是否为空
            return false;
        }
        if(this == obj){     //判断两引用对象是否相等
            return true;
        }
        if(!(obj instanceof Freshman)){  
            //判断作为向上转型实参的对象类型是否与待比较的对象类型相同
            //避免跨类比较
            return false;
        }
        Freshman freshman = (Freshman)obj;
        if(this.name.equals(freshman.name)){
            return true;
        }
        else{
            return false;
        }
    }
}
public class Test3{
    public static void main(String[] args) {
        Freshman freshman1 = new Freshman();
        freshman1.name = "Mike";
        System.out.println(freshman1);
        Freshman freshman2 = new Freshman();   //每次new会开辟新内存
        freshman2.name = "Mike";
        System.out.println(freshman1);
        System.out.println(freshman1 == freshman2); //内存分布是不同对象
        System.out.println(freshman1.equals(freshman2));
    }
}

Ctrl点击查看String类实现的equals方法:

此时再运行,结果为:

注:也可使用编译器的equals方法:

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Freshman freshman = (Freshman) o;
        return Objects.equals(name, freshman.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

第一个equals方法与手动自定义生成的equals方法逻辑几乎相同,此处不再赘述;

第二个重写的是hashcode方法:(此处简单介绍)

之前查看toString方法时,有以下方法:

基于以上Freshman类,试运行main函数内进行如下操作代码:

Freshman freshman1 = new Freshman();
freshman1.name = "Mike";
System.out.println(freshman1);
Freshman freshman2 = new Freshman();   //每次new会开辟新内存
freshman2.name = "Mike";
System.out.println(freshman1);
System.out.println(freshman1.hashCode());
System.out.println(freshman2.hashCode());

输出结果为:

可见编译器认为这两个对象是不一样的对象,如果需要让编译器将以上两个对象认为是同一个对象,就需要重写hashcode方法,而此方法编译器可以自己实现;

3. 内部类

当一个事物的内部还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只对外部事物提供服务,那么这个内部的完整结构最好使用内部类。

在java中可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。

内部类也是封装的一种体现;

3.1 内部类的分类

3.1.1  静态内部类

class OuterClass1{
    //外部类成员属性
    public int data1 = 1;
    private int data2 = 2;
    public static int data3 = 3;
    //静态内部类
    static class InnerClass1{
        //内部类成员属性
        public int data4 = 4;
        private int data5 = 5;
        public static int data6 = 6;
        //内部类成员方法
        public void fun1(){
            System.out.println("Static Inner Class::fun");
            //静态内部类中不能直接访问外部类的非静态成员
            //需要实例化外部类对象,再使用引用进行访问
            OuterClass1 outerClass1 = new OuterClass1();
            System.out.print(outerClass1.data1+" ");
            System.out.print(outerClass1.data2+" ");
            //静态内部类中可以访问外部类的静态成员以及自身的所有成员
            System.out.print(data3+" ");
            System.out.print(data4+" ");
            System.out.print(data5+" ");
            System.out.println(data6);
        }
    }
    //外部类的成员方法
    public void fun2(){
        //外部类可以访问自身成员
        System.out.print(data1+" ");
        System.out.print(data2+" ");
        System.out.print(data3+" ");
        //外部类中可以通过静态内部类实例化的对象引用访问任何限定符修饰的静态内部类的成员变量
        InnerClass1 innerClass1 = new InnerClass1();
        System.out.print(innerClass1.data4+" ");
        System.out.print(innerClass1.data5+" ");
        //外部类中可以使用内部类名访问内部类的静态成员变量
        System.out.println(InnerClass1.data6);
    }

}
public class Test1{
    public static void main(String[] args) {
        OuterClass1.InnerClass1 innerClass1 = new OuterClass1.InnerClass1();
        innerClass1.fun1();
        System.out.println("------------------------");
        OuterClass1 outerClass1 = new OuterClass1();
        outerClass1.fun2();;
    }
}

输出结果为: 

3.1.2  实例内部类 / 非静态内部类

class OuterClass2{
    //外部类成员属性
    public int data1 = 1;
    private int data2 = 2;
    public static int data3 = 3;
    //实例内部类
   class InnerClass2{
        //内部类成员属性
        public int data4 = 4;
        private int data5 = 5;
        //实例内部类中不允许存在静态成员变量
        //public static int data6 = 6;
        //如果需要定义静态变量,需要使用final修饰表示此为常量
        //常量是在编译时就确定的
        public static final int data6 = 6;
        //内部类成员方法
        public void fun1(){
            System.out.println("Inner Class::fun");
            //实例内部类中可以访问所有外部类成员变量
            System.out.print(data1+" ");
            System.out.print(data2+" ");
            System.out.print(data3+" ");
            System.out.print(data4+" ");
            System.out.print(data5+" ");
            System.out.println(data6);
        }
    }
    //外部类的成员方法
    public void fun2(){
        //外部类可以访问自身成员
        System.out.print(data1+" ");
        System.out.print(data2+" ");
        System.out.print(data3+" ");
        //外部类中可以通过实例内部类实例化的对象引用访问任何限定符修饰的实例内部类的成员变量
        InnerClass2 innerClass2 = new InnerClass2();
        System.out.print(innerClass2.data4+" ");
        System.out.print(innerClass2.data5+" ");
        System.out.println(InnerClass2.data6);
    }

}
public class Test1{
    public static void main(String[] args) {
        //需要先实例化外部类对象,才能new内部类对象
        OuterClass2 outerClass2 = new OuterClass2();
        OuterClass2.InnerClass2 innerClass2= outerClass2.new InnerClass2();
        innerClass2.fun1();
        System.out.println("----------------");
        outerClass2.fun2();
    }
}

输出结果为:

注:(1)当内部类、外部类具有同名成员变量时,遵循就近打印原则,比如在上述实例内部类中增加变量:public int data1 = 10;试在内部类成员方法与外部类成员方法中打印data1,输出结果为:

 

如果在此种情况下需要访问外部类的同名成员变量,需要表示为:

System.out.print(OuterClass2.this.data1+" ");

 此时输出结果为:

(2)实例内部类中有两个this,一个是自身的this,还有一个是外部类的this:

class OuterClass2{
    public int data1 = 1;
    private int data2 = 2;
    public static int data3 = 3;

   class InnerClass2{
        public int data1 = 10;
        public int data4 = 4;
        private int data5 = 5;
        public static final int data6 = 6;

        public void fun3(){
            System.out.println(this.data1);
            System.out.println(OuterClass2.this.data1);
        }

    }
}
public class Test1{
    public static void main(String[] args) {
        OuterClass2 outerClass2 = new OuterClass2();
        OuterClass2.InnerClass2 innerClass2= outerClass2.new InnerClass2();
        innerClass2.fun3();
    }
}

输出结果为:

3.1.3 局部内部类

局部内部类是定义在外部类方法体或{}中的类,这种内部类只能在其定义的位置上使用,一般使用的非常少:

class OuterClass3{
    public void fun(){
        class InnerClass3{
            public int a = 1;
            public void test(){
                System.out.println("Hello world.");
            }
        }
        InnerClass3 innerClass3 = new InnerClass3();
        innerClass3.test();
    }
}
public class Test1{
    public static void main(String[] args) {
        OuterClass3 outerClass3 = new OuterClass3();
        outerClass3.fun();
    }
}

输出结果为:

注:① 局部内部类只能在所定义的方法体内部使用;

② 不能被public、static等修饰符修饰;

③ 编译器也有自己独立的字节码文件,命名格式:外部类名$数字内部类名字.class;

 3.1.4 匿名内部类

示例代码1:

interface IA{
    void func();
}
public class Test1{
    public static void main(String[] args) {
        new IA(){
            @Override
            public void func(){
                System.out.println("Hello World.");
            }
        }.func();
    };
}

输出结果为:

示例代码2:

interface IA{
    void func();
}
class AA implements IA{
    @Override
    public void func(){
        System.out.println("hello world.");
    }
}
public class Test1{
    public static void main(String[] args) {
        IA ia = new AA();
        ia.func();
    }
}

输出结果为:

示例代码1展示的是就是匿名内部类,与示例代码2是等效的;

猜你喜欢

转载自blog.csdn.net/m0_63299495/article/details/129900399