04.Java 基础 - 类

基本概念

在面向对象的概念中,我们知道所有的对象都是通过类来描绘的。

在 Java 中类通过关键字 class 来定义。

同样的在 Java 中类具有多种形式,包括普通类、抽象类、内部类。

其中内部类又包含了:成员内部类、局部内部类、匿名内部类、静态内部类。


普通类

这个没什么好说的。

public class Demo { }

抽象类

1.基本概念

上面提到所有的对象都是通过类来描绘的,但是并不是所有的类都是用来描绘对象的。

如果一个类中没有包含足够的信息来描绘一个具体的对象(我们可以理解为一种功能不全的类),这样的类就是抽象类

抽象类通过关键字 absract 定义。它可以包含抽象方法、非抽象方法。


2.实例探究

  • 抽象类定义
public abstract class Parent {

    // 1.成员变量,与普通类无差别
    private  String word="aa";

    // 2.非抽象方法,与普通类无差
    private void talk(){
        System.out.println("Parent is talking");
    }

    // 3.抽象方法,访问权限只能是 public 和 protected 
    abstract void print();
}       
  • 抽象类继承
// 1.抽象类继承抽象类,抽象子类不用实现父类的方法
public abstract class Son extends Parent { }

// 2.普通类继承抽象类,普通子类必须实现父类的所有抽象方法
public class Grandson extends Son {
    @Override
    void print() {
        System.out.println("I am Grandson");
    }
}
  • 抽象类调用
// 错误,抽象类不允许被实例化
//Parent parent = new Parent(); 

通过以上的代码,我们可以总结出这么几点:

  • 抽象类、抽象方法,不能被 private 修饰,且必须使用关键字 abstract 定义。

  • 子类如果不是抽象类,则必须实现父类所有的抽象方法。

  • 抽象类不允许被实例化,编译错误。

  • 抽象类里面也可以包含普通方法,成员变量。


成员内部类

1.基本概念

位于一个类内部的类,被称为成员内部类。

成员内部类具有以下特点:可以访问其外围类的所有属性,而不需要任何特殊条件。

2.实例探究

  • 成员内部类定义:
public class Outter {
    private int a = 10;
    static int b = 20;
    int c = 30;

    // 内部类
    class Inner {
        void print(int d) {
            System.out.println(a + "-" +b+ "-" +c+ "-" + "-" +d;
        }
    }

    // 取得内部类
    Inner getInner() {
        return new Inner();
    }
}
  • 成员内部类调用
Outter out = new Outter();

// 创建内部类的两种方式:
Outter.Inner inner = out.new Inner();
Outter.Inner inner2 = out.getInner();

inner.print(20);

3.原理分析

通过反编译 class 文件,命令如下:

javap -v Outter

执行命令后,后得到两个 class 文件:Outter.Class 和 Outter$Inner.Class。

说明对于虚拟机来说,内部类其实与常规类是一样的。所以 Inner 仍然被编译成一个独立的类,而不是 Outter 类的某一个域。

但是由于成员内部类看起来像是外部类的一个成员,所以可以拥有与成员一样的访问权限。


局部内部类

1.基本概念

局部内部类有两种:

  • 方法内的类。

  • 作用域内的类。

可以将其视作方法或作用域的内局部变量,因此它的访问权限也仅限于方法内或者该作用域内。

同局部变量一样,它是无法被 public、protected、private、static 关键字修饰的。

2.实例探究

public class Man {
    public Object getWoman() {
        // 注意:三个变量都相互不受影响
        int age = 30;

        // 1.方法内的类
        class Woman {
            int age = 20;
        }

        // 2.作用域内的类,此时作用域为 if 
        if(true){
            class Son{
                int age = 10;
            }
        }   
        return new Woman();
    }
}

匿名内部类

1.基本概念

匿名内部类是唯一一种没有构造器的类。因为这个特点,匿名内部类的使用范围非常有限,大部分用于接口回调

它具有以下特点:

  • 匿名内部类在编译的时候由系统自动起名为 Outter$1.class。

  • 匿名内部类一般用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

  • 匿名内部类不能访问外部类方法中的局部变量,除非变量被声明为 final 类型


2.实例探究

public class Test {
    // a 属于全局变量
    int a =100;

    public static void main(String[] args) {
        Test t = new Test();
        t.test(200);
    }

    // b 属于局部变量
    public void test(final int b) {
        // c 属于局部变量
        final int c = 300;

        //  匿名内部类
        new Thread() {
            int  d = 400;
            public void run() {
                System.out.println(a+"-"+b+"-"+c+"-"+d);
            };
        }.start();
    }
}

3.原理分析

通过反编译命令可以得到两个 class 文件:Outter.class 和 Outter$1.class。

关于局部变量的生命周期:

  • 当方法被调用时,局部变量在栈中被创建。当方法运行结束后,退栈,局部变量死亡。

关于内部类对象的声明周期:

  • 创建一个匿名内部类对象,系统为该对象分配内存,直到没有引用变量指向分配给该对象的内存,它才会被 GC 处理。

因此存在情况:

  • 成员方法已调用结束,局部变量已死亡,但匿名内部类的对象仍然活着。

所以在 Java 中采用了 final 关键字+复制的办法来解决:

  • final 关键字:因为它的特性是一旦变量被赋值后,就不能被修改。

  • 复制:在匿名内部类中直接复制了一个与局部变量值的数,让它变成自己的局部变量。

所以当局部变量的生命周期结束后,匿名内部类照样可以访问 final 类型的局部变量,因为它自己拷贝了一份,且与原局部变量的值始终一致。

下面针对上面的代码来分析:

  • 当 test 方法执行完毕之后,变量 b、c 的生命周期就结束了。然而 Thread 对象的生命周期很可能还没有结束。因此要将 b、c 设置为 final 。

  • a 之所以不采用 final 修饰,因为它是全局变量,生命周期是随着类的结束而结束。而类的生命周期肯定大于匿名内部类。


静态内部类

1.基本概念

静态内部类也是定义在一个类里面的类,只不过在类的前面多了一个关键字static

静态内部类和静态成员变量其实具有相同的特点:

  • 它只有类有关,与对象无关。因此可以在没有外部类对象情况下,创建静态内部类。

  • 不能访问外部类的非静态成员或方法


2.实例探究

  • 内部静态类定义:
public class Outter {
    int a = 5;
    static int b = 500;

    // 静态内部类
    static class Inner {
        public Inner() {
            // 只能访问外部类的静态成员
            System.out.println(b);
        }
    }
}
  • 静态内部类调用:
// 静态内部类的调用不依赖外部类对象
Outter.Inner inner= new Outter.Inner();

接口内部类

1.基本概念

接口内部类,顾名思义就是在接口内定义的类。


2.实例探究

  • 接口内部类定义:
public interface People{
    class Man {
        public void print() {
            System.out.println("man.print()...");
        }
    }
}
  • 接口内部类调用:
People.Man  man = new People.Man();

参考

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

oxf

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

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

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

打赏作者

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

抵扣说明:

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

余额充值