[Java] 封装
目录
1. 什么是封装
2. 访问修饰符
3. 封装的好处
4. 封装的步骤
5. 包
5.1 什么是包
5.2 导入包中的类
5.3 自定义包
5.4 常用的包
6. static关键字
6.1 static修饰成员变量
6.2 static修饰成员方法
6.3 Static修饰成员变量初始化
7. 代码块
7.1 普通代码块
7.2 构造代码块
7.3 静态代码块
7.4 各种初始化
8. 对象的打印
1. 什么是封装
封装就是对类里面的成员变量和成员方法进行隐藏,只能在所在类的范围内使用,只留下一些可以被外部访问的方法,供外部调用,隐藏方法实现的内部细节。
这就好比一台电视机,给你提供了一个遥控器供你使用,进行一系列操作,但是操作的内部细节你看不到,只是通过遥控器来实现功能。
2. 访问修饰符
Java当中是通过类和访问权限来实现封装的,类可以实现数据和封装的数据存储在一起,而访问修饰符则可以设置数据的访问权限。
访问限定符有四个:private , default , protected , public 。它们的访问权限也是不一样的。如图所示:
private修饰的属性和方法只能在类内部使用,不能在类外部直接使用。
3. 封装的好处
1.可以隐藏类内部的实现细节。
2.可以保证安全和合理,比如说要对属性设置值,可以在封装的类里面先判断设置的值是否合理,再考虑是否赋值。
4. 封装的步骤
1.对属性和方法进行私有化(只能在类里面使用,不能直接修改值)
2. 设置public的set方法实现对私有的属性的判断和赋值。
3. 设置public的get方法对权限进行判断,进而得到私有属性的值。
举个Student类的例子:
class Student{private String name;private int age;private String stuNum;public void setName(String name) {this.name = name;}public String getName() {return name;}
}public class Test {public static void main(String[] args) {Student student = new Student();student.setName("张三");System.out.println(student.getName());}
}
上面就是设置的两个公共方法一个设置成员变量的值,一个返回成员变量的值。
但是每个成员变量都要写过于麻烦,因此idea提供一种快捷方法,可以直接生成这两个方法:
1.鼠标在类内部右击找到下面的选项:
2. 找到下面选项:
3. 把需要生成的成员变量选上,ok。
这样就实现了间接访问成员变量。
但是为什么把属性设置成了私有还可以被外部间接访问呢?
因为,封装的属性和方法是为类内部的其他方法提供的,而设置的一些公共类的方法就可以被外部所访问,而这个方法有可以使用封装的属性和方法,这样既隐藏了封装的方法的内部细节,又可以通过公共方法给外部间接使用。
5. 包
5.1 什么是包
包本质上是对类的一种管理形式,包里面包含了很多的类,也就相当于包是一个文件夹,里面存放了很多的.Java文件,一个.Java文件对应一个类。
5.2 导入包中的类
这里我们可以用到import关键字进行导入包,也成为打包。例如:
import java.util.Arrays;
java.util就是包的名字,而Arrays就是类名。
当然我们也可以不导入包直接在代码中使用,如下:
int[] a = new int[]{6,4,2,1};java.util.Arrays.sort(a);System.out.println(java.util.Arrays.toString(a));
但是这样书写太麻烦了,但是当两个包里面有相同名字的类都被使用的时候就需要这样去写。
当然还有一种写法是: *叫做通配符
import java.util.*;
不同的包里面可以有相同的类名,这两个类只是名字相同,但是作用不相同。
这种方法可以导入包里面的全部类,但是不建议这样书写。因为有可能在两个包里面会有相同名字的类,这样书写就会导致冲突,编译器会报错。
在java.util包里面有一个Date的类,在java.sql包里面也有一个Date的类,如果书写成如下格式就会报错:
因为这样的话,在代码中使用时,编译器无法识别是哪一个包下面的类,所以遇到这种情况,只能用import导入一个类,另一个类则需要在使用的时候进行导入。如图:
import java.util.Date;Date date = new Date();
java.sql.Date date1 = new java.sql.Date(10);
5.3 自定义包
创建一个包:
包的命必须是字母,数字,点号,下划线,数字不能开头,不能包含关键字。
一般公司命名是以 com.公司名.项目名.模块名 来命名的。
回车后就创建好了一个包:
com是一级目录,abc是二级目录,www是三级目录。Test1是类。
Test1类里面代码如下:
package com.abc.www;public class Test1 {public void test1() {System.out.println("haha");}
}
package是编译器自己生成的,为了声明该类在那个包里面。
下面我自己编写了一段代码。
如果要在其他类中调用自定义包里面的方法,就是如下图:
import com.abc.www.Test1;public class Test {public static void main(String[] args) {Test1 test1 = new Test1();test1.test1();}
}
这样就可以调用了,com.abc.www是包名,Test1是类名,test1是类里面的方法。
5.4 常用的包
java.lang lang是基本包,默认引入的,不需要再导入。
java.util 系统提供的工具包,包含工具类。
java.net 网络开发用的。
java.awt 做Java的页面开发的。
6. static关键字
static是用来修饰成员的,被static修饰的成员叫做静态成员,也叫做类成员,表示该成员不属于任何一个对象的,而是所有对象共同包含成员。我用下面的代码帮助大家理解:
class Student{public String name;public int age;public String stuNum;public static String classNum;public Student(String name, int age, String stuNum) {this.name = name;this.age = age;this.stuNum = stuNum;}
}
public class Test {public static void main(String[] args) {Student student1 = new Student("张三",18,"202311");Student student2 = new Student("李四",19,"202312");}
}
此时学生1和学生2这两个对象都在同一个班级,那么班级属性是两个对象都一样的,就可以被static修饰。
6.1 static修饰成员变量
当static修饰完成员变量后,该成员变量就属于类的变量,而不是方法的变量,此时该静态变量就不会在根据类的定义在堆区申请的对象的内存空间中了,而是在方法区中了。如下图所示关系:
此时我们如何访问静态变量呢?
有两种方式:
Student student1 = new Student("张三",18,"202311");
//通过对象的引用来访问
student1.classNum = "1班";
//通过类名访问
Student.classNum = "1班";
第一种是通过对象的引用来访问,但是不建议使用,因为静态变量不在对象申请的内存中,在方法区中,属于类的变量。
第二种是通过类名来访问。
注意:
- 静态变量的访问不依赖于对象,不需要实例化对象,就可以直接访问。
- 类变量存储在方法区中。
- 类变量的生命周期是整个类的生命周期。
- 静态变量是所有对象共享的,不存在于某个对象的空间里。
6.2 static修饰成员方法
static修饰成员方法后,成员方法变成静态成员方法,静态成员变量的访问不依赖于对象,属于类方法,存在于方法区,有两种访问方式:
class Student {public String name = "张三";public int age = 18;public String sex = "男";public static void print() {System.out.println("hehe");}
}
public class Test1 {public static void main(String[] args) {//方法一Student student = new Student();student.print();//方法二Student.print();}
}
方法一:是通过对象来访问,这种方法虽然不会报错,但是不建议使用,因为静态方法属于类方法,不依赖于对象。
方法二:直接通过类名访问,这种方法建议使用。
注意:
- 静态方法里面不能使用this关键字,因为this关键字依赖于对象,而静态方法的访问不依赖于对象。
- 静态方法不能直接访问非静态方法和非静态变量,但可以通过对象的实例化间接访问。
- 非静态方法可以直接访问静态方法和静态变量,非静态方法依赖于对象,静态方法和静态变量不依赖于对象。
- 不同类间访问静态方法或静态变量,通过类名.方法名或变量名访问。
- 不同类间访问非静态方法或非静态变量,对象实例化后,通过对象来访问。
- static不能修饰局部变量。
6.3 Static修饰成员变量初始化
静态成员方法初始化方法有四种:
- 通过就地初始化,也就是直接初始化值。
- 通过set方法初始化。
- 通过构造方法初始化。
- 通过静态代码块初始化
7. 代码块
代码块是指被{}包含的内容,一般情况下是用来对成员变量进行初始化的。分为三种:
7.1 普通代码块
这是定义在方法内的代码块:
public static void main(String[] args) {//普通代码块{int a = 10;System.out.println(a);}}
7.2 构造代码块
定义在类里面的代码块,也叫做实例代码块。用来初始化非静态成员变量的,在实例化对象的时候执行:
class Student {public String name = "张三";public int age = 18;public static String sex = "男";//实例代码块{name = "李四";age = 20;}
}
7.3 静态代码块
定义在类里面的代码块,用来初始化静态成员变量的,在类加载的时候执行:
class Student {public String name = "张三";public int age = 18;public static String sex = "男";//静态代码块{sex = "女";}
}
静态代码块只能初始化静态成员变量。因为非静态对象初始化依赖于对象。
7.4 各种初始化
我们已经知道了 用构造方法来初始化成员变量,还有就地初始化成员变量,还有代码块初始化成员变量,代码块又分为静态代码块和实例代码块。
那它们的执行先后顺序又是怎样的呢?
多个静态代码块或者多个实例代码块同时初始化,则是按照代码的先后顺序初始化。
静态代码块是在类加载时执行的,而构造方法和实例初始化则是在实例化对象时执行的,所以静态代码块先执行,如果有静态变量就地初始化,会按照先后顺序进行执行,而实例初始化比构造方法先执行。
一般成员变量的定义和初始化先写,在写其他的代码块或者方法。
执行顺序为:就地初始化->静态代码块->实例代码块->构造方法。
如果实例化多个对象时,由于类只会在第一次实例化对象时加载一次,也就是说静态代码块只执行一次,第二次实例化对象时,不执行静态代码块,只执行实例代码块和构造方法。
8. 对象的打印
如果想打印对象的属性也就是成员变量有什么快捷方式吗?
这里idea提供了一种快捷方式,生成toString方法来进行打印:
class Student {public String name = "张三";public int age = 18;public String sex = "男";//打印成员变量的方法:@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}';}
}public class Test1 {public static void main(String[] args) {Student student = new Student();System.out.println(student);}
}
打印结果如下:
这样就可以实现快速打印对象的属性。
如果不写toString方法,默认打印的是地址。当然这种快捷方式不能生成静态成员变量。