• 【Java SE】封装的详解


    目录

    2.3 private 简单使用

    2.2 如何导入包中的类?

    2.2.2 使用 import 导入包

    2.2.3 使用* 发现冲突的情况

    2.2.4 使用 import static 导入静态的方法和字段

    3.1 为什么需要 static 修饰成员?

    3.2 static 修饰成员变量

    3.3 static 修饰静态成员方法

    3.4 static 成员变量初始化

    4.1 代码块的分类及概念

    4.2 构造代码块和静态代码块

    5.1 内部类的概念和分类

    1、封装

    1.1 什么是封装?

    不知道小伙伴对冰箱有没有观察过,市面上大多数的冰箱他们的嵌入式主板,芯片,制冷装置,以及内部电线,应该都没有暴露在我们的视野中,但是他们给我们提供了一些对外可见的接口,比如,插电头,你插上就有电了, 再比如给我们提供了冰箱门上的按钮,你需要操作这几个按钮或者显示屏就能让冰箱帮你干活,那他的地层一些制冷的装置,以及一些功能的机制实现我们都不了解,也从表面上看不到,但是我们却可以通过他们提供的接口去使用,简而言之这就是封装!

    如果把封装的概念放在语言中,那就是:将数据和操作数据的方法进行结合,隐藏对象的属性,和实现细节,仅仅对外公开接口来和对象进行交互。

    2.2 访问权限

    记得之前,写方法写任何东西,我都是写的 public 为什么呢?也浅提了一个,这个是访问权限限定符,既然是封装,就需要限制外部对类或类的属性设置访问权限,在Java中,主要是通过类和访问权限来实现封装, 类可以将数据及实现数据的方法结合在一起,而访问权限用来控制方法或字段能否直接在类外使用,在Java中,一共提供了四种访问限定符:

    private:只允许在同一个包中和同一个类中访问,其他地方均不能访问

    默认:只允许在同一个包中任意位置访问,不同的包,其他地方均不能访问(什么都不写就是默认)

    portected:主要用在继承当中,我们下期讲解

    public:任意地方都能访问

    注意:修饰符除了限定类中的成员,也能限制类的可见性,本期没有讲到的范围,后续会讲到

    2.3 private 简单使用

    class Student {
        //成员变量【属性】
        private String name; //姓名
        private int age; //年龄
        private float score; //分数
        //构造方法
        public Student() {}
        public Student(String name, int age, float score) {
            this.name = name;
            this.age = age;
            this.score = score;
        }
        //成员方法【行为】
        public void studentScores() {
            System.out.println(this.name + "考了" + this.score + "分");
        }
    }
    public class Test {
        public static void main(String[] args) {
            Student student = new Student("张三", 20, 59.9f);
            //这里我将成员变量的访问限定符设置为了 private 代表只能在同一个类中访问
            //student.name = "张三" //error -> 不同类中不能访问
            //student.age = 20; //error -> 不同类中不能访问
            //System.out.println(student.name); //error -> 不同类中不能访问
    
            student.studentScores(); //yes -> 因为这个类成员方法访问限定符是 public
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    这里我们可以简单来看一下 Student 这个类(同一份Java文件,只能有一个被public修饰的类),首先我们把成员变量都设置成的 private 属性,也就代表着,这些成员变量只能在 Student 这个类的内部访问,所以我们下边 Test 类就不能直接通过对象访问他们了,但是可以通过对象访问 studentScores 成员方法,因为他是被 public 修饰的! 一般我们会将成员变量设置为 private,成员方法设置成 public。

    后面我们在讲完包之后,会介绍默认修饰符,也就是什么都不加的情况。

    2、封装之包的概念

    2.1 什么是包?

    女孩子们喜欢买各种包包,主要是背着好看,但是计算机中包可不是这样的,我们可以把文件夹当成一个包,文件夹是用来干嘛的?主要是为了更方便的管理,所以面向对象的体系中,提出了一个软件包的概念,也就是为了更好的管理类,把多个类收集到一起成为一组,成为软件包。

    在Java中,也引入了包,包是对类,结构等封装机制的体现,可以更好组织类和接口等,如果一个包中的类,不想被其他包使用,就修改包中类的权限限定符,同时如果你有相同的类名,你只需要让他们俩处在不同的包里即可!

    2.2 如何导入包中的类?

    记得之前我们了解过输入方法,当我们创建了一个输入对象,编译器则会在最顶部给我们添加一行 import java.util.Scanner; 那如果我们没有这个 import 语句我们要如何用 Scanner 类中的方法呢?

    2.2.1 使用完整类名

    这里我们用一个我们之前没见过的类,我现在要获取一个时间戳,已知 java.util 包中里面 Date类里面有一个 getTime(); 方法可以获取时间戳,假设我们现在不知道 import 语句,也没有添加 import 语句,该如何做到获取时间戳呢?

    public class Test {
        public static void main(String[] args) {
            java.util.Date data = new java.util.Date();
            System.out.println(data.getTime()); //得到一个毫秒级时间戳并打印
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    我们是不是应该这么写,首先我们需要用 Date 类来实例化一个对象,而这个类在 java.util 这个包中,所以我们要通过这个包找到这个类,进行实例化,实例化哪个类呢?还是要通过这个包,里面的类来实例化,最后通过对象来访问类中对应的成员方法进行获取时间戳,并打印!

    2.2.2 使用 import 导入包

    这样写是不是好麻烦,有那么多重复的地方,所以这里就可以使用 import 语句来导入包,所以当我们需要使用 java.util.Date 类里面方法的时候,我们只需要在类的前面 imprt java.util.Date,导入了这个包即可:

    import java.util.Date;
    public class Test {
        public static void main(String[] args) {
            Date data = new Date();
            System.out.println(data.getTime()); //得到一个毫秒级时间戳并打印
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    那如果你还要使用 java.util 里面的其他类呢?其实有一个方法可以不需要一个个导入,直接写成: import java.util.* 只要是 util 里面的公开的类你都可以像上面一样使用,但是我们更推荐显示式的指定要导入的类名,否则容易出现冲突的情况:

    2.2.3 使用* 发现冲突的情况

    import java.util.*
    import java.sql.*
    public class Test {
        public static void main(String[] args) {
            Date data = new Date();
            System.out.println(data.getTime()); //得到一个毫秒级时间戳并打印
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    如上代码会报错,也就是出现冲突,因为 java.sql 和 java.util 这两个包中都有 Date 类,编译器也无法识别你需要使用哪个类,所以在这种情况下,我们就需要使用完整的类名:

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

    2.2.4 使用 import static 导入静态的方法和字段

    至于静态的方法和字段这个我们后面会讲解,这里主要先看下怎么使用,等学了后面内容你就明白了:

    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);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样就可以直接使用方法名了,不需要加类名,但这里必须是静态的方法或者成员才可以,后面会讲到,我们这样用的情况不是很多。

    注意: import 跟 C/C++ 的 include 是不一样的,他们差别很大,#include在编译会展开头文件,是来引用其他文件内容,而Java不需要,import只是为了写代码更方便,更类似于C++ 的 namespace 和 using

    2.3 自定义包

    自定义包也就是我们用户自己建一个包,在IEDA下可以通过右键 src->New->Package 文件的最上方需要加上 package 包名; 来指定该代码在哪个保重,但IDEA编译器会自动帮我们加上,如果你创建的包名叫做 com.lqg.demo,那么则会存在一个对应的路径 com/lqg/demo 来存储代码,要是类中没有 package语句,则表明该类被放在一个默认包中,IDEA对应是 src

    有了上面的一些包的学习,相信你们以及能理解 默认 访问限定符的作用了,下来你可以自己建一个包,然后写一个类,类的修饰符是默认,再去另一个包中就会发现无法使用这个类了,这里就教给大家下去自行学习。

    这里介绍以下 java.util 包,他是java提供的工具程序包,(集合类等),在后续的学习中我们会接触到更多,还有其他的包,随着我们学习的深入慢慢了解。

    3、static修饰符

    3.1 为什么需要 static 修饰成员?

    这里我们来回顾下之前写过的 Student 类,假设我们有三个学生,对应着三个对象,假设这三个学生为张三,李四,王五, 而他们又是同班同学,那我现在需要给类中加一个教室信息,是直接添加一个教室成员变量吗?在之前的类中,每个对象都会包含一份成员变量,称之为实例变量,但是这次我们要添加的教室属性,并不属于某个对象啊,他是属于所有学生对象 共享的 !所以在Java中,static 的出现,就是说,被 static 修饰的成员,称之为静态成员,也可也成为类成员,不属于某个具体的对象,所有对象都能共享!

    3.2 static 修饰成员变量

    这里我们就来给学生类添加一个 static 成员变量,也就是静态成员变量 -> classRoom;

    public class Student {
        //这里表示 name age 成员变量只能在 Student 类中访问
        private String name; 
        private int age;
    
        public static String classRoom = "八年级二班"; //静态成员变量
    
        public static void main(String[] args) {
            Student stu1 = new Student();
            Student stu2 = new Student();
            //静态的不依赖于对象!
            System.out.println(Student.classRoom); //可以直接通过类名访问
            System.out.println(stu1.classRoom); //也可也通过对象访问,但不推荐
            System.out.println(stu2.classRoom); //也可也通过对象访问,但不推荐
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    注意:

    static 修饰的成员变量,不属于某个具体的对象,不存储在某个具体对象的空间中,静态成员变量存储在方法区中,生命周期伴随类的一生,即随类的加载而创建,类的销毁而销毁,他既可以通过类名访问,也可也通过对象访问,但更推荐类名访问方式!

    3.3 static 修饰静态成员方法

    上面看完 static 修饰成员变量,但是我们之前都是建议成员变量用 private 来修饰啊,那这样的话如何在类外访问我们静态成员变量呢?

    有了上面的问题,所以Java中可以用 static 修饰成员方法,如果被 static 修饰的成员方法,称之为静态成员方法,也可成为类方法,不属于任何一个对象, 所以类方法没有隐藏的 this 参数!

    这里我们就把上面的 Student 类里面的类变量改为 private 修饰,增加一个获取教室的类方法:

    public class Student {
        //这里表示 name age 成员变量只能在 Student 类中访问
        private String name; 
        private int age;
    
        public static String classRoom = "八年级二班"; //静态成员变量
    
        public static String getClassRom() {
            return Student.classRom; //类变量,可以通过类名访问,当然在类方法中,也可也省略
        }
    }
    
    class TestStudent {
        public static void main(String[] args) {
            Student stu = new Student();
            System.out.println(Student.getClassRoom()); //可以通过类名访问类方法
            System.out.println(stu.getClassRoom()); //也可也通过对象访问类方法,但不推荐
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    注意:

    static 修饰的成员方法,可以称为类方法,不属于某个具体的对象,在静态成员内部,不能直接访问任何非静态成员变量和方法,前面也说过原因,如果你非要访问,自己用类实例化一个对象吧,但很少这样做,静态方法不支持重写,不能用来实现多态,这个我们后续多态部分时候讲

    3.4 static 成员变量初始化

    一般来说,我们静态成员变量不会在构造方法中初始化,而一般有两种初始化的方式,就地初始化和静态代码块初始化:就地初始化就是在定义的时候直接给出初始值,至于静态代码块初始化,我们往后看代码块的相关知识:

    4、代码块

    4.1 代码块的分类及概念

    代码块就是用 { } 定义的一段代码叫做代码块,根据代码块定义的位置以及关键字,可以分为四种:

    • 普通代码块 -> 这里我们不多强调,就是定义在方法中的代码块,很少用
    • 构造代码块
    • 静态代码块
    • 同步代码块(多线程的时候讲解)

    4.2 构造代码块和静态代码块

    构造代码块,也有构造方法两个字,那他跟构造方法是有一定区别的,构造代码块是定义在类中的代码块(不加修饰符) ,也可称为实例代码块,主要是用于初始化实例成员变量,也就是非静态成员变量。

    静态代码块,主要是用来初始化静态成员变量的代码块,我们来看具体演示代码:

    public class Student {
        private String name;
        private int age;
        private static String classRoom;
    
        {
            //这里是构造代码块,也叫实例代码块,一般用于初始化实例成员变量
            this.name = "王五";
            System.out.println("构造代码块执行!");
        }
    
        static {
            //这里是静态代码块,一般用于初始化静态成员变量
            Student.classRoom = "八年级二班";
            System.out.println("静态代码块执行!");
        }
    }
    class TestStudent {
        public static void main(String[] args) {
            Student stu1 = new Student();
            Student stu2 = new Student();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    我们 new 了两个对象,而静态代码块是定义在构造代码块后面的,我们再来看结果执行main方法的结果:

    为什么会是这个结果?为什么静态代码块后定义,但是先执行?为什么构造代码块执行了两次?这里我们一一来解释:

    • 实例代码块每次创建对象都会执行一次,静态代码块不管生成多少个对象只执行一次。
    • 静态成员是类的属性,因此是在JVM加载类时开辟空间的,所以静态代码块要比实例代码块先执行
    • 如果一个类包含多个静态代码块,则按照定义的顺序,也就是从上往下执行(合并)

    5、内部类

    5.1 内部类的概念和分类

    什么是内部类?将一个类定义在另一个类或者一个方法的内部,称为内部类,内部类外面的类可以称为外部类,内部类也是封装的一种体现!

    内部类必须定义在外部类的 { } 内部,内部类和外部类是共用一个Java源文件,但是编译之后,内部类会形成单独的字节码文件,这个大家可以在编译之后自行查看。

    内部类分为两大类:成员内部类(定义位置跟成员所处位置相同),局部内部类(定义在方法体或者 {} 中)

    成员内部类包含: 实例内部类,静态内部类

    局部内部类包含: 局部内部类,匿名内部类(抽象类和接口部分讲)

    5.2 实例内部类

    public class Student {
        private String name;
        private int age;
        private static String classRoom;
    
        class InnerClass {
            //实例内部类,可以直接访问外部类中:任意访问限定修饰符的成员
            int age = Student.this.age; //如果访问外部类同名的成员,需要使用类名.this.成员名
            String room = Student.classRoom;
    
            //实例内部类中,不能定义有static修饰的成员,如果非要定义,则需要使用 final 修饰的常量
            //常量是在程序编译的时候确定的,一旦初始化,就不能改变
            private static final int a = 10;
            public void func() {
                //实例内部类的非静态方法中,包含了一个指向外部类对象的引用,所以可以使用this.访问
                this.age = 10;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    实例内部类就是指没有被 static 修饰的内部类,那么如何实例化内部类呢?

    public static void main(String[] args) {
            //方法1
            Student stu1 = new Student();
            Student.InnerClass stu2 = stu1.new InnerClass();
            //方法2
            Student.InnerClass stu3 = new Student().new InnerClass();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    方法1,既然是实例内部类它的定义位置是跟外部类的成员位置相同,我们可以先有一个外部类的对象,所以也就可以借助外部类的对象来实例化内部类。

    方法2,我们可以直接先实例化外部类,在紧接着通过实例化的外部类再去实例化内部类,这样也是可以的!

    总结:
    • 外部类的任何成员都可以在实例内部类中 直接访问 !
    • 实例内部类所以他所处的位置是于外部类成员位置相同的,因此也可以使用public private限定符来约束
    • 在实例方法中,访问相同的成员时,优先访问自己的,如果要访问外部类的,得使用 类名.this.同名成员 来访问
    • 实例内部类非静态方法中,包含了一个指向外部类的对象的引用
    • 实例内部类不能有static修饰的成员,如果非要,需要用 final 修饰(以后讲解)
    • 如果外部类要访问内部类的成员,必须要先有内部类的对象

    5.3 静态内部类

    public class Student {
        private String name;
        private int age;
        private static String classRoom;
        public static String getClassRoom() {
            return Student.classRoom;
        }
    
        static class StaticInnerClass {
            //静态类中,可以直接访问外部类的静态成员
            String room = Student.classRoom;
            public void func() {
                String room = Student.getClassRoom();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    我们创建静态类对象呢?首先既然他是静态的那就说明他不依赖于对象,也就是说,不需要先创建外部对象那我们就可以直接使用外部类类名.内部静态类类名来实例化:

    public static void main(String[] args) {
            Student.StaticInnerClass sInClass = new Student.StaticInnerClass();
        }
    
    • 1
    • 2
    • 3

    关于静态类我们还要注意一点:静态类中不能直接访问外部类非静态成员,如果非要访问外部类非静态成员,我们需要先创建外部类对象,这个也交给小伙伴们下去尝试。

    5.4 局部内部类

    局部内部类也就是定义在外部类的方法体中或者 {} 中,这种内只能在定义的地方使用,一般使用非常的少,因为是局部的,他并不能被public,static等修饰符修饰,他也有自己独立字节码文件:外部类类名$数字内部类类名.class,这种几乎不使用,我们知道有就可以。

    最后,内部类主要是出现在库中代码里面,实际开发用的不算很多,用的最多的是匿名内部类,但是这个我们放到抽象类和接口部分介绍。

  • 相关阅读:
    解决RabbitMQ报错Stats in management UI are disabled on this node
    理解Mysql索引原理及特性
    这样在 C# 使用 LongRunnigTask 是错的
    Gradle多渠道打包
    Tomcat
    12-1- GAN -简单网络-线性网络
    微信小程序环境搭建
    大数据队列Kafka
    使用Laravel框架创建项目
    HikariCP源码阅读笔记
  • 原文地址:https://blog.csdn.net/Huangjiazhen711/article/details/126699839