JavaSE 封装

1. 封装

1.1 封装的概念

封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互

举个例子,我们正在使用着的电脑,对于用户来说只需要通过键盘、鼠标、显示器就能成与计算机之间的交互,进而完成日常事务。

但实际上:电脑真正在工作的却是CPU、显卡、内存等一些硬件元件。

在这里插入图片描述

对于计算机使用者而言,不用关心内部有什么核心部件,只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。

对于程序而言也可进行这样的操作,如:通过private限定符修饰成员变量,然后通过特定的方法来调用这个私有变量

1.2 访问限定符

Java中主要通过类和访问权限来实现封装类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认识,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

在这里插入图片描述

  • public:任何情况下都可被访问
  • protected:不同包中的非子类无法访问,其它情况都可以
  • default:什么都不写时的默认权限,只能在同一个里被访问
  • private:只能在同一个中的同一个类被访问(什么是包,往下翻)
  • 访问权限除了可以限定类中成员的可见性,也可以控制类的可见性

代码示例:

public class Computer {
    private String cpu; //CPU
    private String memory; //内存
    public String screen; //屏幕
    String brand; //品牌  没有加限定符默认为default

    public Computer(String cpu, String memory, String screen, String brand) {
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
        this.brand = brand;
    }

    public void Boot() {
        System.out.println("开机");
    }
    public void PowerOff() {
        System.out.println("关机");
    }

    public void SurfInternet() {
        System.out.println("上网");
    }

}
class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer("HW","i7","25G","13*14");
        System.out.println(computer.brand); //default属性:只能被本包中类访问
        System.out.println(computer.screen);//public属性:可以被任何其他类访问
//        System.out.println(computer.cpu); private属性:只能在Computer类中访问,不能被其他类访问
        computer.Boot();
        computer.PowerOff();
        computer.SurfInternet();
    }
}

在这里插入图片描述

注:一般情况下成员变量设置为private,成员方法设置为public

1.3 封装扩展之包

1.3.1 包的概念

为了更好的管理类,把多个类收集在一起称为一组,称为软件包

在Java中,包是对类、接口等封装机制的体现,是一种对类或者接口等很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可

1.3.2 导入包中的类

Java中已经提供了很多现成的类供我们使用,如Date类:可以使用java.util.Date导入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());
    }
}

但是这种写法比较麻烦,可以使用import语句导入包:

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

在这里插入图片描述

如果需要使用java.util中的其他类,可以使用import java.util.*,但更建议显示的指定要导入的类名,否则容易出现冲突

import java.util.*;
//import java.sql.*; 若加上这条语句。util和sql中都存在一个Date这样的类,此时就会出现歧义,编译出错
public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getTime());
    }
}

可以使用import static导入包中静态的方法和字段:

import static java.lang.Math.*;
public class Test1 {
    public static void main(String[] args) {
        double x = 30.0;
        double y = 40.0;

        //double ret = Math.sqrt(Math.pow(x,2) + Math.pow(y,2)); 相比之下静态导入的方式更加方便
        double ret = sqrt(pow(x,2) + pow(y,2));
        System.out.println(ret);
    }
}

在这里插入图片描述

1.3.3 自定义包

操作步骤:

  1. 在IDEA中新建一个包:右键src -> 新建 -> 包:

    在这里插入图片描述

    1. 在弹出的对话框中输入包名:

      在这里插入图片描述

  2. 在包中创建类,右键包名 -> 新建 -> 类,然后输入类名即可:

    在这里插入图片描述

  3. 此时可以看到我们的磁盘上的目录结构已经被IDEA自动创建出来了

在这里插入图片描述

  1. 同时我们也能看到,在新创建的Test1.java文件的最上方就出现了一个package语句

    在这里插入图片描述

1.3.4 包的访问权限控制举例

这里直接上代码示例,其中Computer类位于demo1包中,TestComputer类位于demo2包中:

package demo1;

public class Computer {
    private String cpu; //CPU
    private String memory; //内存
    public String screen; //屏幕
    String brand; //品牌

    public Computer(String cpu, String memory, String screen, String brand) {
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
        this.brand = brand;
    }

    public void Boot() {
        System.out.println("开机");
    }
    public void PowerOff() {
        System.out.println("关机");
    }

    public void SurfInternet() {
        System.out.println("上网");
    }

}
package demo2;

import demo1.Computer;

public class TestComputer {
    public static void main(String[] args) {
        Computer computer = new Computer("HW","i7","25G","13*14");
        System.out.println(computer.screen);//public属性:可以被任何其他类访问
        System.out.println(computer.brand); //default属性:只能被本包中类访问,此处会报错       
		System.out.println(computer.cpu); //private属性:只能在Computer类中访问,不能被其他类访问,此处会报错
        computer.Boot();
        computer.PowerOff();
        computer.SurfInternet();
    }
}

在这里插入图片描述

注:

  • public属性:可以被任何其他类访问
  • default属性:只能被本包中类访问,此处会报错
  • private属性:只能在Computer类中访问,不能被其他类访问,此处会报错
  • 如果去掉Computer类之前的public修饰符,代码也会编译失败
1.3.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编程开发包

2. static成员

先引入一段代码:

public class Student {
    private String name;
    private int age;
    private String gender;
    private double score;

    public Student(String name, int age, String gender, double score) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.score = score;
    }

    public static void main(String[] args) {
        Student s1 = new Student("Li lei",18,"男",3.8);
        Student s2 = new Student("Han MeiMei",19,"女",4.0);
        Student s3 = new Student("Jim",18,"男",2.6);
    }
}

在这里定义了一个学生类,并进行了实例化

在这里插入图片描述

假设这三个同学是一个班的,且上课地点相同,那能否给类中再加一个成员变量来保存这个教室?可以,不过要给它加上static来进行修饰,这样这间教室就能让所有学生来共享了,而不单是一个对象独享这间教室

2.1 static修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的

代码示例:

public class Student {
    private String name;
    private int age;
    private String gender;
    private double score;

    public static String classroom = "J1707";

    public Student(String name, int age, String gender, double score) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.score = score;
    }

    public static void main(String[] args) {
        Student s1 = new Student("Li lei",18,"男",3.8);
        Student s2 = new Student("Han MeiMei",19,"女",4.0);
        Student s3 = new Student("Jim",18,"男",2.6);

        //静态成员变量可直接通过类名访问
        System.out.println(Student.classroom);

        //也可通过对象访问,单classroom是三个对象共享的
        System.out.println(s1.classroom);
        System.out.println(s2.classroom);
        System.out.println(s3.classroom);
    }


}

在这里插入图片描述

静态成员变量特性:

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但更推荐使用类名访问
  3. 类变量存储在方法区中
  4. 生命周期伴随类的一生,随类的加载而创建,随类的卸载而销毁
  5. 被static修饰的变量不能是方法中的变量,为类变量

2.2 static修饰成员方法

一般类中的数据成员我们都设置为private,而成员方法设置为public,那设置之后,Student类中classroom属性如何在类外访问呢?

在这里插入图片描述

在类外调用会报错,那这个属性又该调用?

在Java中,被static修饰的成员方法称为静态成员方法,是类的方法,表示某个对象所特有的。而静态成员变量一般是通过静态方法来访问的

public class Student {
    private String name;
    private int age;
    private String gender;
    private double score;

    private static String classroom = "J1707";

    public Student(String name, int age, String gender, double score) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.score = score;
    }

    public static String getClassroom() {
        return classroom;
    }
}


//注:这个是在src中重新定义的一个类
public class TestStudent {
    public static void main(String[] args) {
        System.out.println(Student.getClassroom());
    }
}

//执行结果
J1707

在这里插入图片描述

静态方法特性:

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过**类名.静态方法名()**调用
  3. 不能在静态方法中访问任何非静态成员变量
public static String getClassRoom() {
	this.age += 1; //不能在静态方法中访问任何非静态成员变量
    return classRoom;
}
//编译失败 java: 无法从静态上下文中引用非静态 变量 age
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时无法传递this引用
public static String getClassRoom() {
	doClass();
	return classRoom;
}
//编译报错 java: 无法从静态上下文中引用非静态 方法 doClass()

2.3 static成员变量初始化

静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性

静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化

  1. 就地初始化:定义时直接给初始值

    public class Student {
        private String name;
        private int age;
        private String gender;
        private double score;
        private static String classroom = "J1707";
    }
    
  2. 静态代码块初始化:那什么是代码块,往下看~

3. 代码块

3.1 代码块概念及分类

使用{}定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

  • 普通代码块
  • 构造块
  • 静态块
  • 同步代码块

3.2 普通代码块

定义在方法中的代码块(较少用)

代码示例:

public class Main {
    public static void main(String[] args) {
        {
            int x = 10;
            System.out.println("X1 = " + x);
        }
        int x = 100;
        System.out.println("x2 = " + x);
    }

}

//执行结果
X1 = 10
x2 = 100

在这里插入图片描述

3.3 构造代码块

定义在类中的代码块(不加修饰符),也叫实例代码块一般用于初始化实例成员变量

代码示例:

package demo3;

public class Student {

    private String name;
    private int age;
    private String gender;
    private double score;

    public Student() {
        System.out.println("I am Studnet init()!");
    }

    //实例代码块(构造代码块)
    {
        this.name = "Mike";
        this.age = 18;
        this.gender = "man";
        this.score = 95;
        System.out.println("I am instance init()!");
    }

    public void show() {
        System.out.println("name: " + name + " age: " + age + " gender " + gender + " score: " + score);
    }

}


package demo3;

public class Main {
    public static void main(String[] args) {
        Student student = new Student();
        student.show();
    }
}

//执行结果
I am instance init()!
I am Studnet init()!
name: Mike age: 18 gender man score: 95.0

在这里插入图片描述

3.4 静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量

代码示例:

package demo3;

public class Student {

    private String name;
    private int age;
    private String gender;
    private double score;
    public static String classRoom;
    public Student() {
        System.out.println("I am Studnet init()!");
    }

    //实例代码块(构造代码块)
    {
        this.name = "Mike";
        this.age = 18;
        this.gender = "man";
        this.score = 95;
        System.out.println("I am instance init()!");
    }

    //静态代码块
    static {
        classRoom = "J1707";
        System.out.println("I am static init()!");
    }

    public void show() {
        System.out.println("name: " + name + " age: " + age + " gender " + gender + " score: " + score);
    }

}
/
package demo3;

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println("======================");
        Student s2 = new Student();

    }
}

//执行结果
I am static init()!
I am instance init()!
I am Studnet init()!
======================
I am instance init()!
I am Studnet init()!  

在这里插入图片描述

从执行结果可以看出,代码块中存在着执行顺序:

  1. 静态代码块的先执行
  2. 然后执行构造代码块
  3. 再执行对应的构造方法

注:

  • 静态代码块不管生成多少个对象,只执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  • 如果一个类中包含多个静态代码块,在编译时,编译器会按照定义的先后次序依次执行(合并)
  • 构造代码块只有在创建对象时才会执行

4. 内部类

4.1 内部类的定义

**在Java中,可以将一个类定义在另一个类或者一个方法的内部,签字称为内部类,后者称未外部类。**内部类也是封装的一种体现。

public class OutClass {
	class InnerClass {
	
	}
}
//OutClass是外部类
//InnerClass是内部类

注:

  • 定义在class类名{}花括号外部的,即使是在一个文件里,也不能称为内部类
public class A {
	
}

class B {
	
}

//A和B是两个独立的类,彼此之间没有关系
  • 内部类和外部类共用一个java源文件,但是经过编译之后,内部类会形成独立的字节码文件

4.2 内部类的分类

根据内部类定义的位置不同,内部类一般可以分为以下几种形式:

  1. 成员内部类:
    • 实例内部类:未被static修饰的成员内部类
    • 静态内部类:被static修饰的成员内部类
  2. 局部内部类(几乎不会使用)、匿名内部类

代码示例:

public class OutClass {
  	//成员位置定义:未被static修饰-->实例内部类
	public class InnerClass1 {
	
	}
	
	//成员位置定义:被static修饰 --> 静态内部类
	static class InnerClass2 {
	
	}
	
	public void method() {
		//方法中也可以定义内部类 -->局部内部类,几乎不用
		class InnerClass3 {
		
		}
	}
}

4.2 实例内部类

在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类而未被static修饰的成员内部类被称为实例内部类

package demo4;

public class OutClass {
    private int a;
    static int b;
    int c;
    public void methodA() {
        a = 10;
        System.out.println(a);
    }

    public static void methodB() {
        System.out.println(b);
    }

    // 实例内部类:未被static修饰
    class InnerClass {
        int c;
        public void methodInner() {
            //在实例内部类中可以直接访问外部类中(任意访问限定符)修饰的成员
            a = 100;
            b = 200;
            methodA();
            methodB();

            //如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
            c = 300;
            System.out.println(c);

            //如果要访问外部类同名成员时,必须用 外部类名称.this.同名成员名字
            OutClass.this.c = 400;
            System.out.println(OutClass.this.c);
        }
    }

    public static void main(String[] args) {

        //外部类:对象创建以及成员访问
        OutClass outClass = new OutClass();
        System.out.println(outClass.a); //0
        System.out.println(outClass.b); //0
        System.out.println(outClass.c); //0
        outClass.methodA(); //10
        outClass.methodB(); //0

        System.out.println("=============实例内部类的访问=============");
        /*要访问实例内部类中成员,必须要创建实例内部类的对象*/
        /*而实例内部类定义与外部内部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类*/

        //创建实例内部类对象
        OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
        //可以先将外部类对象创建出来,然后再创建实例内部类对象
        OutClass.InnerClass innerClass2 = outClass.new InnerClass();
        innerClass2.methodInner();
    }
}

//执行结果
0
0
0
10
0
=============实例内部类的访问=============
10
200
300
400

注:

  1. 外部类中的任何成员都可以在实例内部类方法中直接访问
  2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在实例内部类方法中访问同名的成员时,优先访问内部类中的成员,如果要访问外部类同名的成员,通过外部类名称.this.同名名称来访问
  4. 实例内部类对象必须在先有外部类对象的前提下才能创建
  5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
  6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象

4.3 静态内部类

被static修饰的内部成员类称为静态内部类

代码示例:

package demo4;

public class OutClass {
    private int a;
    static int b;
    int c;
    
    public static void methodB() {
        System.out.println(b);
    }

    //静态内部类:被static修饰
    static class InnerClass2 {
        public void methodInner() {
            //在该内部类中只能访问外部的静态成员
//            a = 100; //编译失败,因为a不是静态成员
            b = 200;
//            methodA(); //编译失败,因为methodA()不是静态方法
            methodB();
        }
    }


    public static void main(String[] args) {
        //静态内部类对象创建以及成员访问
        OutClass.InnerClass2 innerClass21 = new OutClass.InnerClass2();
        innerClass21.methodInner();
    }
}

在这里插入图片描述

注:

  1. 在静态内部类中只能访问外部类中的静态成员
  2. 创建静态内部类对象时,不需要先创建外部类对象

5. 对象的打印

若用println()来打印对象:

package demo5;

public class Person {
    String name;
    String gender;
    int age;

    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    public static void main(String[] args) {
        Person person = new Person("Mike","男",18);
        System.out.println(person);
    }
}

//执行结果
demo5.Person@1b6d3586

执行结果并不是我们想要的对象的属性,这里我们可以重写toString方法(快捷键:Alt + Insert 选择toString())

package demo5;

public class Person {
    String name;
    String gender;
    int age;

    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        Person person = new Person("Mike","男",18);
        System.out.println(person);
    }
}

//执行结果
Person{name='Mike', gender='男', age=18}

在这里插入图片描述
这样就能打印出我们想要的对象属性了!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值