【Java】类与对象(下)(继承、多态、抽象、接口)

目录

一、包

1、导入包中的类

1.1、使用的时候导入

1.2、用 import 语句导入

1.3、 用 java.util.* 导入

2、静态导入

3、将类放在包中

 具体操作步骤

4、包的访问控制权限

5、常见的系统包

二、继承

1、基本语法

2、方法的重写

3、 super 关键字

(1)使用 super 关键字调用父类的成员变量与成员方法

(2)使用 super 关键字调用父类的构造方法

4、protected 关键字及四种访问权限

5、final关键字

三、抽象类

四、接口

1、接口的定义

2、接口的实现类

五、多态

1、多态的概述

2、向上转型

4、向下转型

instanceof 关键字

5、动态绑定

6、内部类

6.1、成员内部类

6.2、局部内部类

6.3、静态内部类

6.4、匿名内部类


上一节讲了有关类与对象初步认知的一些知识——实例化、构造方法、this和static关键字、代码块、类的封装以及匿名对象。

如果还有不了解的小伙伴可以传送这篇——【Java】类与对象(上)

这一节讲的语法就抽象很多了,难度也更大,细碎的东西非常多——大致包括final关键字类的继承抽象类接口以及多态的知识,为了写这篇博客整合了半天,话不多说,我们开始吧——

一、包

(package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性。

1、导入包中的类

1.1、使用的时候导入

在要使用的 Java 提供的类的前面加上 java.包名.类 。如下例,导入 java.util 包中的 Date 类

public class Test {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        System.out.println(date.getTime());
   }
}

1.2、用 import 语句导入

如下例, 在最开头使用import导入后,就可以直接用 Java提供的类了。

import java.util.Date;
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
   }
}

1.3、 用 java.util.* 导入

  java.util.* 导入其它所有这个包下的类,这个顾名思义,如果用了之后,只要是在这个包下的类都可以直接使用。用起来非常方便,但是也有个缺陷——在使用多个包的情况下,很可能会导致 包中存在同名的类,调用的时候产生冲突的情况。
import java.util.*;
import java.sql.*;
public class Test {
    public static void main(String[] args) {
        // util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
        Date date = new Date();
        System.out.println(date.getTime());
   }
}
// 编译出错
Error:(5, 9) java: 对Date的引用不明确
  java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配

如上例,这种时候要特殊指定一下类,将第六行语句改成 

java.util.Date date = new java.util.Date();

即可编译通过。

2、静态导入

使用 import static 可以导入包中的静态的方法和字段。这样可以使代码更简洁。
例一:
import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
        out.println("hello");
   }
}

例二:

import static java.lang.Math.*;
public class Test {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;
        //如果没有导入则需要写成 double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
   }
}

3、将类放在包中

(a)在文件的最上方加上一个 package 语句指定该代码在哪个包中。如果一个类没有 package 语句则该类被放到一个默认包中。

(b)包名需要尽量指定成唯一的名字。

(c)包名要和代码路径相匹配,例如创建 com.csdn.demo 的包那么会存在一个对应的路径 com/csdn/demo 来存储代码。

 具体操作步骤

1、在idea中新建一个包。

 2、在对话框中输入包名。

3、在该包中创建类。

 4、此时,我们就可以发现磁盘上的目录结构已经被 IDEA 自动创建出来了,且新建的文件中就出现了package语句。

 

4、包的访问控制权限

我们已经了解了类中的 public private。public是公开,哪里都能访问;而private 中的成员只能被该类的内部使用。但如果某个成员不包含任何 关键字 此时这个成员默认为包访问权限——即只能被包内部的其他类使用,而不能被包外部的类使用。

5、常见的系统包

1. java.lang 系统常用 基础类 (String Object), 此包从 JDK1.1 后自动导入。
2. java.lang.reflect :java 反射编程 包。
3. java.net 进行 网络编程 开发包。
4. java.sql 进行 数据库开发 的支持包。
5. java.util java 提供的 工具程序 包。 ( 集合类等 )
6. java.io :I/O 编程开发 包。

二、继承

在现实生活中,说到继承,多会想到子女继承父辈的财产、事业等等。在程序中,继承描述的是事物之间的所属关系。

在Java中,类的继承是指在一个现有的类基础上去构建一个新的类,构建出来的新类被称作子类派生类,现有的类被称作父类基类超类,子类会自动拥有父类所有可以继承的属性和方法。

1、基本语法

[修饰符] class 子类 extends 父类 {
  
}

❤ 子类的实例中,也包含着父类的实例。可以使用 super 关键字得到父类实例的引用。

❤ 子类会继承父类的所有 public 的字段和方法。对于父类的 private 的字段和方法子类中是无法访问的。 

❤ 类的修饰符是可选的,用来指定类的访问权限。如果不写则默认为包访问权限

❤ Java 中一个子类只能继承一个父类C++/Python等语言支持多继承),但是多个类可以继承同一个父类,也可以多层继承(套娃了属于是)

接下来我们看一个例子帮助我们更好理解:

class Animal { 
 public String name; 
 public Animal(String name) { 
 this.name = name; 
 } 
 public void eat(String food) { 
 System.out.println(this.name + "正在吃" + food); 
 } 
} 

class Cat extends Animal { 
 public Cat(String name) { 
 super(name);  // 使用 super 调用父类的构造方法(等会就会讲到) 
 } 
} 

class Bird extends Animal { 
 public Bird(String name) { 
 super(name); 
 } 
 public void fly() { 
 System.out.println(this.name + "正在飞"); 
 } 
} 

public class Test { 
 public static void main(String[] args) { 
 Cat cat = new Cat("小喵"); 
 cat.eat("猫粮"); 
 Bird bird = new Bird("圆圆"); 
 bird.fly(); 
 } 
}

//运行结果
小喵正在吃猫粮
圆圆正在飞

2、方法的重写

子类实现父类的同名方法 , 并且 参数的类型和个数完全相同 , 这种情况称 覆写 / 重写 / 覆盖 (Override)

关于重写的注意事项
1、子类中重写的方法需要和父类被重写的方法具有 相同的方法名参数列表以及 返回值类型。 
2、 普通方法可以重写 , static 修饰的 静态方法不能重写
3、子类重写父类方法时, 不能使用 比父类中被重写的方法更严格的访问权限
另外 , 针对重写的方法 , 可以使用 @Override 注解来显式指定。
有了这个注解能帮我们进行一些合法性校验 例如不小心将方法名字拼写错了 ( 比如写成 aet), 那么此时编译器就会发 现父类中没有 aet 方法 , 就会编译报错 提示无法构成重写

3、 super 关键字

当子类重写父类的方法后,子类对象将无法直接访问父类被重写的方法。为了解决这个问题,在Java中专门提供了一个关键字来访问父类的成员——成员变量、成员方法和构造方法。

(1)使用 super 关键字调用父类的成员变量与成员方法

具体格式:

super.成员变量

super.成员方法(参数1,参数2....)

 接下来通过个案例看看如何使用 super 关键字:

class Animal{
    String name="动物";
    void shout(){
        System.out.println("动物发出叫声");
    }
}

class Dog extends Animal{
    String name="犬类";
    void shout(){
        super.shout();
    }
    void printName(){
        System.out.println("name="+super.name);
    }
}

public class Test{
    public static void main(String[] args) {
        Dog dog=new Dog();
        dog.shout();
        dog.printName();
    }
}

//运行结果
动物发出叫声
name=动物

(2)使用 super 关键字调用父类的构造方法

具体格式:

super(参数1,参数2...)

 接下来通过一个案例来学习如何使用super关键字调用父类的构造方法:

class Animal{
  public Animal(String name){
      System.out.println("我是一只"+name);
  }
}

class Dog extends Animal{
  public Dog(){
      super("沙皮狗");
  }
}

public class Test{
    public static void main(String[] args) {
        Dog dog=new Dog();
    }
}

//运行结果
我是一只沙皮狗

注意事项:

1、通过 super 调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。

2、在子类的构造方法中一定会调用父类的某个构造方法。如果没有指定,在实例化子类对象时,会默认调用父类无参的构造方法。(此时就很容易出错,如果父类只定义了有参的构造方法,而子类没有显示的调用父类的构造方法时,就会出现编译错误)

因此,为了防止这种情况的发生,我们在定义一个类时,如果没有特殊需求,当定义了有参构造方法后,尽量在类中再显式地定义一个无参的构造方法,这样可以避免该类被继承时出现错误。

到这,用法就讲的差不多了。此时,我们回过头想想,this 和 super 有哪些区别呢?

区别

thissuper
概念访问本类中的属性和方法由子类访问父类的属性和方法
查找范围先查找本类,如果本类没有就调用父类不查找本类而直接调用父类

4、protected 关键字及四种访问权限

对于类的调用者来说 ,protected 修饰的字段和方法是不能访问的;对于类的子类 同一个包的其他类 来说 ,protected 修饰的字段和方法是可以访问的。
至此,我们了解了Java 中对于字段和方法共有四种访问权限,如下表所示:
范围privatedefaultprotectedpublic
同一包中的同一类
同一包中的不同类
不同包中的子类
不同包中的非子类

5、final关键字

基本用法:

1、[修饰符] final 返回类型 方法名(){ }

2、final 类型 变量名

 final关键字可以用于修饰类、变量和方法,它有“不可更改”或者“最终的含义”,因此被 final 修饰的类、变量和方法有以下特性:

(1)final 修饰的类不能被继承

(2)final 修饰的方法不能被子类重写

(3)final 修饰的变量(成员变量和局部变量)是常量,只能赋值一次

而修饰变量时,又分两种情况:

1、当 final 修饰局部变量时,可以在声明变量的同时对变量进行赋值,也可以先声明变量然后再进行有且只有一次的赋值。

2、当 final 修饰成员变量时,在声明变量的同时必须进行初始化赋值,否则程序编译报错。

三、抽象类

当定义一个类的时候,常需要定义一些方法来描述该类的行为,但有时候这些方法的实现方式是无法确定的,此时就可以用到抽象类来满足这种需求——能使一个类包含所需的方法,又无须提供其方法的实现。

抽象类以及抽象方法的基本语法格式如下:

[修饰符] abstract class 类名{

[修饰符]  abstract  方法返回值类型  方法名 ([参数列表]);

}

 注意:

1、包含抽象方法的类必须定义为抽象类,但抽象类中可以不包含任何抽象方法。

2、抽象类是不可以被实例化的(因为抽象类中有可能包含抽象方法,抽象方法没有方法体,不可以被调用);如果想调用抽象类中定义的抽象方法,需要创建一个子类,在子类中实现抽象类中的抽象方法。

3、抽象方法不能是 private 的。(无法被重写)

4、抽象类中可以包含其他的非抽象方法,也可以包含字段。这个非抽象方法和普通方法的规则都是一样的可以被重写,也可以被子类直接调用。

5、抽象类与抽象方法都不能被 final 修饰。(抽象类就是为了被继承而存在,被 final 修饰就无法被继承,同理抽象方法也不能被重写 )

6、

  • 一个普通类,继承了一个抽象类,那么这个普通类当中,需要重写这个抽象类中的所有抽象方法。
  • 一个抽象类A,继承了一个抽象类B,那么这个抽象类A,可以不实现抽象父类B的抽象方法。
  • 如果当抽象类 A 再次被一个普通类继承时,这个普通类必须重写A和B两个抽象类中的所有抽象方法。(颇有种“出来混,迟早是要还的”的感觉)

 四、接口

1、接口的定义

接口是抽象类的更进一步 抽象类中还可以包含非抽象方法 和字段 而接口中包含的方法都是抽象方法,字段只能包含静态常量。这是之前对接口的定义。
在JDK 8 中,对接口进行了重新定义,接口除了抽象方法外,还可以有 默认方法静态方法(也叫类方法),默认方法使用 default 修饰,静态方法使用 static 修饰,并且这两种方法都允许有方法体。
接口定义的基本语法如下:
[修饰符] interface 接口名 [extends 父接口1,父接口2...]{
     
      [public] [static] [final] 常量类型 常量名 = 常量值 ;
      [public] [abstract] 返回值类型 方法名(参数列表);
      [public] default 返回值类型 方法名(参数列表){
                 //默认方法的方法体
}
      [public] static 返回值类型 方法名(参数列表){
                //静态方法的方法体
}
}

 在上述语法格式中:

1、"[ ]"中的内容都是可选的,修饰符可以使用 public 或者直接省略(省略则默认采用包访问权限)

2、“extends 父接口1,父接口2” 表示定义一个接口时,可以同时继承多个父接口,这也是为了解决类的单继承的限制。

3、常量定义时必须进行初始化赋值,定义默认方法与静态方法时,可以有方法体。

4、接口不能单独被实例化(抽象类都不能,接口显然更不能了)

5、在接口定义常量时,可以省略 “public static final” 修饰符。与此类似,定义抽象方法时,也可以省略 “public abstract” 修饰符,定义默认方法与静态方法时,可以省略 “public” 修饰符。这些修饰符系统都会默认添加。

2、接口的实现类

基本语法:

[修饰符] class 类名 [extends 父类名 ] [implements 接口1,接口2] {

                                        .......

}

 接下来用一个实例演示一下:

interface Animal{
    int ID=1;
    void breathe();
    default void getType(String type){
        System.out.println("该动物属于:"+type);
    }
    static int getId(){
        return Animal.ID;
    }
}

class Dog implements Animal{
    public void breathe(){
        System.out.println("狗在呼吸");
    }
}

public class Main{
    public static void main(String[] args) {
        System.out.println(Animal.getId());
        Dog dog=new Dog();
        System.out.println(dog.ID);
        dog.breathe();
        dog.getType("犬科");
    }
}

//运行结果
1
1
狗在呼吸
该动物属于:犬科

上述例子演示的是类与接口之间的关系。其实,接口与接口之间还可以是继承关系,接口中的继承同样使用 extends 关键字来实现。接下来对上述例子进行修改演示接口之间的继承关系。

interface Animal{
    int ID=1;
    void breathe();
    default void getType(String type){
        System.out.println("该动物属于:"+type);
    }
    static int getId(){
        return Animal.ID;
    }
}

interface LandAnimal extends Animal{
    void run();
}

class Dog implements LandAnimal{
    public void breathe(){
        System.out.println("狗在呼吸");
    }

    public void run(){
        System.out.println("狗在陆地上跑");
    }
}

public  class Main{
    public static void main(String[] args) {
        System.out.println(Animal.getId());
        Dog dog=new Dog();
        System.out.println(dog.ID);
        dog.breathe();
        dog.getType("犬科");
        dog.run();
    }
}

//运行结果
1
1
狗在呼吸
该动物属于:犬科
狗在陆地上跑

注意:

1、接口名一般用 I+英文名 ,比较好区分。

2、接口中有三类方法:静态方法、抽象方法和默认方法。静态方法可以通过“接口名.方法名”的形式来调用;而抽象方法默认方法只能通过接口实现类的实例对象来调用。

3、

  • 当一个类实现接口时,如果这个类是抽象类,只需实现接口中的部分抽象方法即可。如果一个非抽象类实现接口时,则需要实现接口中的所有抽象方法。
  • 如果一个接口A继承了另一个接口B,当一个普通类实现接口A的时候,则需要实现两个接口中包含的所有抽象方法。(与之前讲到的的类的继承有异曲同工之妙~)

4、一个类可以通过 implement 关键字同时实现多个接口。接口之间可以通过 extends 关键字实现继承,并且一个接口可以同时继承多个接口。

5、当一个类实现一个接口后,重写的这个方法前面必须加上 public 。(思考一下这是为什么呢?答案很简单,但是需要串联前面的知识——因为子类重写的方法的限制不能比父类更严格,而父类,也就是接口,包含的类都是 public 的,所以子类重写的方法前当然得加上 public )

6、一个类在继承一个类的同时还可以实现接口,此时,extends 关键字必须位于 implement 关键字之前。

class A extends B implements C{

                 .....//先继承,再实现

}

五、多态

1、多态的概述

在Java中,多态是指不同类的对象在调用同一个方法时所呈现出的多种不同行为。通常来说,在一个类中定义的属性和方法被其他类继承或重写后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法将呈现多种不同形态。

Java的多态性是由类的继承方法重写以及父类引用指向子类对象体现的。由于一个父类可以有多个子类对象,多个子类都可以重写父类的方法,并且多个不同子类对象也可以指向同一个父类;这样程序运行时才能知道具体代表的是哪个子类对象。这就体现了多态性。

2、向上转型

我们先看下面一段代码:

abstract class Animal{
    abstract void shout();
}

class Cat extends Animal{
    public void shout(){
        System.out.println("喵喵!");
      }
}

class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪!");
    }
}

public class Main{
    public static void main(String[] args) {
        Animal an1=new Cat();
        Animal an2=new Dog();
        an1.shout();
        an2.shout();
    }
}

//运行结果
喵喵!
汪汪!

由上述程序,我们可以看出程序在编译时自动识别具体的子类对象,从而选择性的调用对应的方法,这就是Java中多态性的体现。

上述程序还涉及了一个很重要的点——将子类对象当作父类类型使用,而这种情况在Java中被称为“向上转型”。如下:

        Animal an1=new Cat( );//将Cat类对象当作Animal类型来使用
        Animal an2=new Dog();//将Dog类对象当作Animal类型来使用

 为啥叫“向上转型”呢?

在面向对象程序设计中 针对一些复杂的场景, 程序猿们会画一种 UML 图的方式来表示类之间的关系。 此时父类通常画在子类的上方 所以我们就称为 " 向上转型 " , 表示往父类的方向转。
例如左图这个样子的,父类在子类之上,故得名为向上转型。

4、向下转型

看一段代码:

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

 class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
     public void fly() {
         System.out.println(this.name + "正在飞");
     }
}

public class Main{
    public static void main(String[] args) {
        Animal animal = new Bird("哈哈");
        animal.eat("谷子");
        // (Bird) 表示强制类型转换
        Bird bird = (Bird)animal;
        bird.fly();
    }
}

//运行结果
我是一只小鸟
哈哈正在吃谷子
哈哈正在飞

分析 “Animal animal = new Bird("哈哈"); ”  这行代码,我们可以得出:

1、编译器检查有哪些方法存在, 看的是 Animal 这个类型。

2、执行时究竟执行父类的方法还是子类的方法, 看的是 Bird 这个类型。

3、将子类对象当作父类使用时,不需要任何显式声明,但是此时不能通过父类变量去调用子类特有的方法。如果非要调用,则需“向下转型”。

但是有时候,这个向下转型又不大靠谱,比如:

Animal animal = new Cat(" 小猫 ");
Bird bird = (Bird)animal;
bird.fly();
 // animal 本质上引用的是一个 Cat 对象 , 是不能转成 Bird 对象的。
所以引入一个关键字 instanceof,它可以判断一个对象是否为某个类(或接口)的实例/子类实例。

instanceof 关键字

语法格式如下:

对象(或对象引用变量)instanceof 类(或接口)
则上述代码,我们可以更改为:
Animal animal = new Cat("小猫");
if(animal instanceof Bird){
    Bird bird = (Bird)animal;
    bird.fly();
}else{
    System.out.println("该类型的对象不是 Bird 类型!")
}

这时再进行类型转换就安全多了。

5、动态绑定

当子类和父类中出现同名方法的时候 , 再去调用会出现什么情况呢?接下来我们看一段代码:
class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
    public void eat(String food) {
        System.out.println("我是一只小动物");
        System.out.println(this.name + "正在吃" + food);
    }
}

 class Bird extends Animal {
    public Bird(String name) {
        super(name);
    }
    public void eat(String food) {
        System.out.println("我是一只小鸟");
        System.out.println(this.name + "正在吃" + food);
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Animal("哈哈");
        animal1.eat("谷子");
        Animal animal2 = new Bird("嘿嘿");
        animal2.eat("谷子");
    }
}

//运行结果
我是一只小动物
哈哈正在吃谷子
我是一只小鸟
嘿嘿正在吃谷子

由上述程序运行结果来看,我们发现:

1、animal1 animal2 虽然都是 Animal 类型的引用, 但是 animal1 指向 Animal 类型的实例, animal2 指向 Bird 类型的实例。

2、针对 animal1 animal2 分别调用 eat 方法 , 发现 animal1.eat() 实际调用了父类的方法 ,
animal2.eat() 实际调用了子类的方法。
因此 , 调用某个类的方法 究竟执行了哪段代码 ( 是父类方法的代码还是子类方法的代码 ), 要看究竟 这个引用指向的是父类对象还是子类对象 这个过程是 程序运行时 决定的 ( 而不是编译期 ), 因此称为 动态绑定

 6、内部类

在一个类的内部定义类,这样的类称作内部类。这个内部类所在的类称为外部类。在实际情况中,内部类分为四种形式:成员内部类局部内部类静态内部类、和 匿名内部类

6.1、成员内部类

在一个类中定义的类,被称为成员内部类。在成员内部类中,可以访问外部类的所有成员,包括成员变量和成员方法;在外部类中,同样可以访问成员内部类的变量和方法。

例:

class Outer{
    int m=0;
    void test1(){
        System.out.println("外部类成员方法!");
    }

    class Inner{
        int n=1;
        void show1(){
            System.out.println("外部成员变量 m="+m);
            test1();
        }

        void show2(){
            System.out.println("内部成员方法!");
        }
    }
    void test2(){
        Inner inner=new Inner();
        System.out.println("内部成员变量 n="+inner.n);
        inner.show2();
    }
}


public class Main{
    public static void main(String[] args) {
        Outer outer=new Outer();
        Outer.Inner inner=outer.new Inner();
        inner.show1();
        outer.test2();
    }
}

//运行结果
外部成员变量 m=0
外部类成员方法!
内部成员变量 n=1
内部成员方法!

上述要注意的是,“ Outer.Inner inner=outer.new Inner( ); ”  是通过外部类对象创建的内部类对象,这样就可以操作内部类中的成员。

创建内部类对象的具体格式如下:

外部类名.内部类名 变量名 = new 外部类名( ).new 内部类名( );

或  外部类名.内部类名 变量名 = 外部对象名.new 内部类名( );

6.2、局部内部类

局部内部类,又名方法内部类,就是定义在某个局部范围中的类,它和局部变量一样,都是在方法中定义的,其有效范围只限于方法内部

在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中的变量和方法却只能在创建该局部内部类的方法中进行访问。

接下来看个例子如何运用:

class Outer{
    int m=0;
    void test1(){
        System.out.println("外部类成员方法!");
    }
    void test2(){
        class Inner{
            int n=1;
            void show(){
                System.out.println("外部类变量 m="+m);
                test1();
            }
        }
        Inner inner=new Inner();
        System.out.println("局部内部变量 n="+inner.n);
        inner.show();
    }
}

public class Main{
    public static void main(String[] args) {
       Outer outer=new Outer();
       outer.test2();
    }
}

//运行结果
局部内部变量 n=1
外部类变量 m=0
外部类成员方法!

上述例子再一次验证了:

局部内部类可以访问外部类的所有成员,而只有在包含局部内部类的方法中才可以访问局部内部类的所有成员。

6.3、静态内部类

静态内部类,就是用 static 关键字修饰的成员内部类。在功能上,与成员内部类相比,静态内部类中只能访问外部类的静态成员。如果通过外部类访问静态内部类成员时,可以跳过外部类而直接通过内部类访问静态内部类成员。

1、创建静态内部类对象的基本语法格式为:

在非外部类中:外部类名.静态内部类名  变量名 = new 外部类名.静态内部类名 ( );

在外部类中:内部类名 变量名 = new 内部类名  ( ) ;

2、外部类调用静态内部类中的属性和方法:

(1)通过创建静态内部类实例来调用其非静态属性方法

(2)通过 内部类.属性  /  内部类.方法   的方式直接调用其静态属性方法

看个例子帮助我们更好理解其用法(最好上手写一遍会更快掌握):

class Outer{
    static int m=0;

     static class Inner{
       int n=2;
       static int i=5;
       void show(){
          System.out.println("外部类变量 m="+m);//在静态内部类中访问外部类的成员变量
       }
       static void show2(){
           System.out.println("调用了inner的静态方法!");
       }
    }

    public void test(){
        Inner inner=new Inner();
        System.out.println(inner.n);//访问静态内部类的非静态变量
        inner.show();//访问静态内部类的非静态方法
    }
    public void display(){
        System.out.println(Inner.i);//调用静态内部类的静态成员变量
        Inner.show2();//调用静态内部类的静态方法
    }


}

public class Main{
    public static void main(String[] args) {
       Outer.Inner inner=new Outer.Inner();
       inner.show(); //在非外部类中调用静态内部类的非静态方法
       Outer outer=new Outer();
       outer.test();
       outer.display();
    }
}

//运行结果
外部类变量 m=0
2
外部类变量 m=0
5
调用了inner的静态方法!

6.4、匿名内部类

在Java中调用某个方法时,如果该方法的参数是一个接口类型,除了可以传入一个参数接口实现类,还可以使用匿名内部类实现接口来作为该方法的参数。匿名内部类也就是没有名字的内部类。正因为没有名字,所以匿名内部类只能使用一次(跟匿名对象有相似之处),但使用匿名内部类还有个前提:必须继承一个父类或实现一个接口。

abstract class Animal {
    public abstract void test();
}

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

        //第一种使用匿名内部类的方法
        Animal rabbit= new Animal() {
            public void test() {
                System.out.println("第一种调用方法");
            }
        };
        rabbit.test();
        
        //第二种使用匿名内部类的方法
        new Animal(){
            public void test(){
                System.out.println("第二种调用方法");
            }
        }.test();
    }
}

//运行结果
第一种调用方法
第二种调用方法

在上述例子中,相信大家应该对匿名内部类有了初步的了解,但是初学者不要求完全掌握这种写法,只需理解这些语法即可。

===================

最后再回顾、总结一下抽象类和接口——

区别抽象类接口
组成普通类 + 抽象方法抽象方法 + 静态、默认方法(可带方法体)+ 全局常量
权限public、protected、default(默认为包访问权限)全为public
子类使用使用extends关键字继承抽象类使用 implements关键字实现接口
子类限制一个子类只能继承一个抽象类一个子类可以实现多个接口
联系一个抽象类可以实现多个接口接口不能继承抽象类,但是能用extends继承多个父接口

==============================

这节就到这里啦,我们下节再见!

to be continue →

欢迎大家互相探讨交流补充 欢迎捉虫!

欢迎在学习Java的小伙伴互关,一起成长进步~

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玫瑰与鸢尾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值