第19篇:Java 中的 final 关键字、嵌套类、内部类、静态嵌套类、局部类

简介: 📝 有效 final:虽然没有被final修饰,但只进行了一次赋值(若被赋值了不止一次,则不是有效 final)📝 从 Java8 开始,如果局部变量没有被第二次赋值,则该局部变量会被认定为是【有效 final】

零、关于继承的补充

❓ 子类对象的内存中是否包含父类中定义的private成员变量 ?

📋 。只是不能够直接访问而已,可让父类提供 public的成员方法以访问 private的成员变量。

一、final

✏️ 被final修饰的不能被继承
✏️ 被final修饰的方法不能被重写
✏️ 被final修饰的变量只能进行1次赋值

📋 final可修饰局部变量
📋 final可修饰成员变量

✏️ Java 中的常量(Constant)指 :同时被staticfinal修饰的成员变量

📋 若将 基本类型字符串定义为常量( static final),并且 编译时就能确定值:编译器会使用 常量值替换各处的 常量名(类似 C 语言中的 宏替换
📋 这种常量被称作 编译时常量(compile-time constant)

二、嵌套类(Nested Class)

✏️ 嵌套类:定义在另一类中的类

public class OuterClass {
    /* 静态嵌套类 */
    static class StaticNestedClass {

    }

    /* 非静态嵌套类(内部类) */
    class InnerClass {
        
    }
}

✏️ 没有被static修饰的嵌套类叫做内部类
✏️ 嵌套类外层的类叫做外部类(Outer Class)
✏️ 最外层的外部类叫做顶级类(Top-level Class)

三、内部类(Inner Class)

✏️ 内部类:没有被static修饰的嵌套类(也叫做非静态嵌套类)
✏️ 与实例变量、实例方法一样,内部类与外部类的实例挂钩

✒️ 必须先创建外部类实例,然后通过 外部类实例创建内部类实例

✏️ 内部类不能定义编译时常量之外的任何static成员
在这里插入图片描述
在这里插入图片描述
✏️ 内部类可以直接访问外部类中的所有成员(即使是private成员)
✏️ 外部类可以直接访问内部类实例的成员变量和方法(即使是private


先创建外部类实例,然后通过外部类实例创建内部类实例:

public class Person {
    /* Person 类的一个内部类(没有被 static 修饰) */
    public class Hand {

    }
}

class TestDemo {
    public static void main(String[] args) {
        /* 先创建外部类实例 */
        Person p = new Person();
        // 人 com.gq.Person@1540e19d
        System.out.println("\n人 " + p);

        /* 通过外部类实例创建内部类实例 */
        Person.Hand hand = p.new Hand();
        // 手 com.gq.Person$Hand@677327b6
        System.out.println("手 " + hand);
    }
}

内部类可以直接访问外部类中的所有成员(即使是 private 成员)
外部类可以直接访问内部类实例中的成员变量和成员方法(即使是 private)

public class Computer {
    private int price = 8989;
    public static String brand = "苹果";

    private void showComputer(Mouse mouse) {
        System.out.println("Computer - openComputer()");
        System.out.println("mouse.mousePrice = " + mouse.mousePrice);
        mouse.printMouse();
    }

    public class Mouse {
        private int mousePrice = 111;

        private void printMouse() {
            System.out.println("printMouse()_Mouse.mousePrice = " + mousePrice);
        }

        public void printOuterClassMember() {
            System.out.println("\nComputer.price = " + price);
            System.out.println("Computer_static_brand: " + brand);
            showComputer(this);
        }
    }
}

class DemoTest {
    public static void main(String[] args) {
        Computer computer = new Computer();

        Computer.Mouse mouse = computer.new Mouse();
        /*
            Computer.price = 8989
            Computer_static_brand: 苹果
            Computer - openComputer()
            mouse.mousePrice = 111
            printMouse()_Mouse.mousePrice = 111
         */
        mouse.printOuterClassMember();
    }
}

外部类的成员变量和内部类的成员变量重名的时候,如何访问外部类的成员变量?

public class OuterClass {
    private int n = 1;

    public class InnerClass { /* 内部类 */
        private int n = 0;

        public void test() {
            System.out.println(n); // 0
            System.out.println(this.n); // 0
            // 访问外部类的 n 属性
            System.out.println(OuterClass.this.n); // 1
        }
    }
}

四、内部类内存布局

✏️ 通过外部类实例创建内部类实例后,内部类实例的内存中会有一个指针指向外部类实例
画下面代码的内存布局图:

public class Computer {
    public int price = 8989;

    public class Mouse { /* 内部类 */
        public String color = "黑";
    }
}

class DemoTest {
    public static void main(String[] args) {
        Computer computer = new Computer();
        /* 创建内部类实例 */
        Computer.Mouse mouse1 = computer.new Mouse();
        Computer.Mouse mouse2 = computer.new Mouse();

        // computer.price = 8989
        System.out.println("\ncomputer.price = " + computer.price);
        // mouse.color = 黑
        System.out.println("mouse.color = " + mouse1.color);
    }
}

在这里插入图片描述

五、静态嵌套类(Static Nested Class)

✏️ 静态嵌套类:被static修饰的嵌套类

✏️ 静态嵌套类在行为上就是一个顶级类,只是定义的代码写在了另一个类中

✏️ 与一般的顶级类不同,静态嵌套类有一些特殊权限

📝 可以直接访问或调用 外部类中的 静态成员(即使是 private
📝 若想访问或调用 外部类中的 实例成员必须先创建外部类的实例,然后通过外部类的实例访问外部类的实例成员(即使是 private成员)

普通顶级类相比静态嵌套类的区别1:

public class Person {
    private String name = "张浩男";
    private int age = 16;
    private static final char GENDER = '男';
    private static String father = "庆医";
    private static int familyNo = 9521;

    private void play() {
        System.out.println("Person_play()");
    }
}
✏️ 上面的 Person 类中的属性 name、属性 age 和方法 play 都被 private关键字修饰
✏️ 在其他类中无法访问 Person 类中的私有成员变量或私有方法(如下图)
在这里插入图片描述

普通顶级类相比静态嵌套类的区别2(静态嵌套类的好处):

public class Person {
    private String name = "张浩男";
    private int age = 16;

    private static final char GENDER = '男';
    private static String father = "庆医";
    private static int familyNo = 9521;

    private void play() {
        System.out.println("Person_play()");
    }

    public static void run() {
        System.out.println("Person_run()");
    }

    /* 静态嵌套类 */
    public static class Phone {
        public void showInfo() {
            System.out.println("\n----------- showInfo()");

            /* 静态嵌套类可直接访问外部类的静态成员(即使是 private 成员) */
            System.out.println("GENDER = " + GENDER);
            System.out.println("father = " + father);
            System.out.println("familyNo = " + familyNo);
            run();

            System.out.println();

            /* 创建外部类实例后, 才可访问外部类的实例成员(即使是 private 成员) */
            Person person = new Person();
            System.out.println(person.name);
            System.out.println(person.age);
            person.play();

            System.out.println("----------- showInfo()");
        }
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Person.Phone phone = new Person.Phone();
        phone.showInfo();
        
        /*
            output:
                ----------- showInfo()
                GENDER = 男
                father = 庆医
                familyNo = 9521
                Person_run()
                
                张浩男
                16
                Person_play()
                ----------- showInfo()
         */
    }
}

六、什么情况使用嵌套类?

✏️ 若类 A 只在类 C 内部被使用,可以考虑把类 A 嵌套到类 C 中

📝 封装性更好
📝 程序包更加简洁
📝 增加可读性和维护性

✏️ 若类 A 需要经常访问类 C 中的非共有成员(被private修饰的成员),可以考虑将类 A 嵌套到类 C 中

📝 可根据需要将类 A 隐藏起来(不堆外暴露)

✏️ 若需要经常访问非公共的实例成员,设计成内部类(非静态嵌套类),否则设计成静态嵌套类

📝 若必须先有 C 实例才能创建 A 实例,那么可以考虑把 A 设计为 C 的内部类

七、局部类(Local Class)

✏️ 定义在代码块中的类叫局部类(可定义在方法中,for循环中,if语句中)

📝 You can define a local class inside any block. You can define a local class in a method body, a for loop, or an if clause.

✏️ 局部类的作用域只在定义它的代码块中

✏️ 局部类中不能定义除编译时常量之外的其他static成员

📝 编译时常量:若将基本类型或字符串定义为常量( static final),并且在编译时就能确定值,则编译器会使用常量值替换各处的常量名(类似 C 语言中的宏替换)
📝 被 static修饰的成员可以通过类名访问或调用,但局部类的作用域只在定义它的代码块中,产生矛盾。

✏️ 局部类内部只能访问final有效 final 的局部变量

📝 有效 final:虽然没有被 final修饰,但只进行了 一次赋值(若被赋值了不止一次,则不是有效 final
📝 从 Java8 开始,如果局部变量没有被第二次赋值,则该局部变量会被认定为是【有效 final】

在这里插入图片描述
在这里插入图片描述

✏️ 局部类可以直接访问外部类中的所有成员(即使是private成员)

📝 局部类只有定义在实例相关的代码块中才能直接访问外部类中的实例成员(实例变量、实例方法)
public class OuterClass {
    private String outerName = "龙行天下";

    public void testMethod() {
        // 定义在代码块中的变量叫局部变量
        int number = 123;
        // 若没有第二次给 number 赋值, number 就是有效 final 的局部变量
        // number = 5;

        final int count = 666;

        class LocalClassTest { /* 一个局部类 */
            // num 是一个编译时常量
            private static final int num = 22;

            private void show() {
                System.out.println("LocalClassTest_show()");
                System.out.println("number = " + number);
                System.out.println("count = " + count);
                System.out.println("outerName = " + outerName);
            }
        }

        LocalClassTest localClass = new LocalClassTest();
        localClass.show();
        System.out.println(LocalClassTest.num);
    }
}

class TestDemo {
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.testMethod();
    }
}
🎁 结束:如有错误,请不吝赐教!
相关文章
|
3天前
|
存储 缓存 Java
Java 并发编程——volatile 关键字解析
本文介绍了Java线程中的`volatile`关键字及其与`synchronized`锁的区别。`volatile`保证了变量的可见性和一定的有序性,但不能保证原子性。它通过内存屏障实现,避免指令重排序,确保线程间数据一致。相比`synchronized`,`volatile`性能更优,适用于简单状态标记和某些特定场景,如单例模式中的双重检查锁定。文中还解释了Java内存模型的基本概念,包括主内存、工作内存及并发编程中的原子性、可见性和有序性。
Java 并发编程——volatile 关键字解析
|
1月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
143 57
|
3天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
3天前
|
缓存 安全 Java
Java volatile关键字:你真的懂了吗?
`volatile` 是 Java 中的轻量级同步机制,主要用于保证多线程环境下共享变量的可见性和防止指令重排。它确保一个线程对 `volatile` 变量的修改能立即被其他线程看到,但不能保证原子性。典型应用场景包括状态标记、双重检查锁定和安全发布对象等。`volatile` 适用于布尔型、字节型等简单类型及引用类型,不适用于 `long` 和 `double` 类型。与 `synchronized` 不同,`volatile` 不提供互斥性,因此在需要互斥的场景下不能替代 `synchronized`。
2043 3
|
1月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
52 8
|
1月前
|
JavaScript 前端开发 Java
java中的this关键字
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。自学前端2年半,正向全栈进发。若我的文章对你有帮助,欢迎关注,持续更新中!🎉🎉🎉
51 9
|
1月前
|
设计模式 JavaScript 前端开发
java中的static关键字
欢迎来到瑞雨溪的博客,博主是一名热爱JavaScript和Vue的大一学生,致力于全栈开发。如果你从我的文章中受益,欢迎关注我,将持续分享更多优质内容。你的支持是我前进的动力!🎉🎉🎉
51 8
|
1月前
|
Java Android开发
Eclipse 创建 Java 类
Eclipse 创建 Java 类
28 0
|
7月前
|
Java
Java中final关键字(看这篇就够了)
Java中final关键字(看这篇就够了)
72 0
|
2月前
|
Java 编译器
在Java中,关于final、static关键字与方法的重写和继承【易错点】
在Java中,关于final、static关键字与方法的重写和继承【易错点】
31 5