第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();
    }
}
🎁 结束:如有错误,请不吝赐教!
相关文章
|
4天前
|
Java 关系型数据库 MySQL
Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
【4月更文挑战第12天】Elasticsearch【问题记录 01】启动服务&停止服务的2类方法【及 java.nio.file.AccessDeniedException: xx/pid 问题解决】(含shell脚本文件)
30 3
|
22小时前
|
人工智能 安全 Java
Java8 - LocalDateTime时间日期类使用详解
Java8 - LocalDateTime时间日期类使用详解
|
2天前
|
安全 Java 程序员
|
2天前
|
安全 Java 编译器
是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。
15 0
|
2天前
|
Java
两千字讲明白java中instanceof关键字的使用!
两千字讲明白java中instanceof关键字的使用!
11 0
|
2天前
|
Java 开发者
Java基础知识整理,注释、关键字、运算符
在日常的工作中,总会遇到很多大段的代码,逻辑复杂,看得人云山雾绕,这时候若能言简意赅的加上注释,会让阅读者豁然开朗,这就是注释的魅力!
37 11
|
2天前
|
Java
Java Class类
Java Class类
8 0
|
7天前
|
安全 Java 开发者
Java并发编程:深入理解Synchronized关键字
【4月更文挑战第19天】 在Java多线程编程中,为了确保数据的一致性和线程安全,我们经常需要使用到同步机制。其中,`synchronized`关键字是最为常见的一种方式,它能够保证在同一时刻只有一个线程可以访问某个对象的特定代码段。本文将深入探讨`synchronized`关键字的原理、用法以及性能影响,并通过具体示例来展示如何在Java程序中有效地应用这一技术。
|
9天前
|
Java 编译器
Java Character 类
4月更文挑战第13天
|
10天前
|
存储 Java
Java基础教程(7)-Java中的面向对象和类
【4月更文挑战第7天】Java是面向对象编程(OOP)语言,强调将事务抽象成对象。面向对象与面向过程的区别在于,前者通过对象间的交互解决问题,后者按步骤顺序执行。类是对象的模板,对象是类的实例。创建类使用`class`关键字,对象通过`new`运算符动态分配内存。方法包括构造函数和一般方法,构造函数用于对象初始化,一般方法处理逻辑。方法可以有0个或多个参数,可变参数用`类型...`定义。`this`关键字用于访问当前对象的属性。