前言
面向对象中最基本的概念:类、对象、属性、行为。这些概念只是构成面向对象最最基本的模板,还有很多更进一步的细节度和丰富度。 这些更进一步的丰富度和细节主要是通过面向对象的4个特征进行定义和描述的。 这4个特征就是:封装、继承、多态和抽象。它们不是统一维度平行关系,而是具有一定的顺序关联的。所以,封装是所有的基础。
目录
一、封装
作用:提高了程序的安全性,保护数据;隐藏代码的实现细节;统一接口;增强系统的可维护性
“封装”顾名思义,就是“封”和“装”,两个动作。
装
其实类的定义中的那对{},就是装的实现。
public class JiangXiaoBai{
//属性(常量变量)
//初始化块
//构造
//行为(功能块)
//内部类
}
- 装 -- 指代了我们可以在一个类当中定义哪些内容。除了属性和行为这些最本质的,直接与数据的存放和操作相关,另外我们今天加的构造也好,实例初始化也好都是辅助内容。他们是为了更方便我们的对象的生成,以及数据的初始化绑定而设计的。
—— 构造方法
类名 对象名 = new 类名()中 " 类名() " 其实就是一种特殊的方法,它叫做:构造方法。
public Student(){
}
- 作用
- 本质作用是用于创建对象
- 可以在创建对象的时候,直接给成员变量赋值
public class Student {
//成员变量
public String name;
public String age;
public Student() {
}
//构造方法
public Student(String name, String age) {
//通过局部变量给 成员变量赋值
this.name = name;
this.age = age;
}
}
测试
public class Test {
public static void main(String[] args) {
//不用带参构造方法 new了一个对象后 还得用点操作 去一个个赋值
Student student=new Student();
student.name="学生";
student.age="20";
//通过带参构造 new了之后直接初始化值
Student student1=new Student("学生1","18");
System.out.println(student.name+student.age);
}
}
- 语法
在默认的情况下,Java的编译器会自动为我们没有书写构造方法的类提供一个默认的构造方法。 那么,这个构造方法长啥样子呢?
1、构造方法的名字必须是类名;
2、构造方法没有返回类型,连void都没有;
3、默认的构造方法是public的;
4、默认的构造方法是无参的;
5、如果我们自己定义了构造方法,那么编译器就不会再默认生成公共无参构造了。
6、构造方法允许方法的重载。方法重载:指的是在同一个类当中,具有多个同名方法,为了能够区分,要求参数列表不同(包括:参数类型、参数个数、参数顺序的不同) 以上,是我们平时书写构造方法的语法。
- 步骤
构造方法的调用---new
而一旦new了之后,构造方法就会默认完成以下动作:
- 在内存堆区,划分空间;空间大小是由这个对象所属类的定义决定的。 解释一下:JVM会把内存至少划分为4个区域:堆区、栈区、数据段、代码段(方法区)。
-
在该空间,划分属性;(根据属性分类)
-
对属性进行初始化,默认情况下,基本数据类型属性初始化为0,引用数据类型属性初始化为null;
-
执行你在构造方法里面书写的代码。 提问:既然构造方法里面一句代码不写,这个对象也创建好了,那么我们还需要写构造吗? 构造方法对于初学者来说,需要书写的代码就是接收外部的参数,跟属性赋值,方便外面的调用者。
练习提示: 构造方法的书写位置在属性之后,方法之前; 提供带参构造是根据外部的使用者可以允许传递哪些参数决定的; 根据标准Java类的书写规范,一旦定义了带参构造,那么编译器不会提供公共无参构造,所以要求开发人员必须手工书写一个公共无参构造。
堆区 --- new出来的对象
栈区 --- 局部变量
数据段 --- 常量放在数据段,通常都是在加载期放入的
代码段 --- 可执行的代码指令
—— 初始化块
语法:它的语法就像一个没有方法声明的方法块,只有{ }和内容。
{
//实例初始化块
}
作用
初始化 类属性 和 对象属性
实例初始化块
这个实例初始化块在执行的时机上,它是在产生对象的时候会被默认执行。也就是说每产生一个对象,就会被执行一次。所以,从执行时间上它和构造方法有重叠。
初始化块和构造方法的执行顺序: 误区:通过打印有些人会认为是先执行的初始化块,后执行的构造方法。 在实例初始化块当中,当前对象已经产生了,当前对象的属性已经划分了,甚至这个属性的值已经完成了默认初始化了,所以实例初始化块应该说是在构造方法前三步动作之后,最后一步执行构造方法{}里面代码之前。 这个尴尬的执行时机和位置,导致了实例初始化块很少被使用到。
静态初始化块
用static修饰的初始化块,修饰后叫做“静态初始化块”。
普通的实例初始化块,很少用,因为它的执行时机和执行内容是与构造方法有重合的。但是,静态初始化块常常能够看到。 静态初始化块是在加载期加载这个类的时候被执行,且一个类只需要被加载一次,所以它也就只执行一次。 这个特点导致静态初始化块的执行时机是独一无二的,在稍微有点规模的项目当中,都会出现。我们往往把要在执行前的准备工作,写在静态初始化块当中。让程序在运行前先完成这些准备动作。
注意:静态初始化当中只能操作静态的属性(行为),不能操作到非静态的。
静态初始化块是在加载期被执行的,这个时候还没有执行到产生对象的代码,而非静态的内容都是跟对象进行绑定的,所以我们无法在静态代码块中指定this是谁,也就无法告知JVM这些静态属性是哪个对象身上的。
public class Student {
public String name; //普通属性
public static String jingTaiName; //静态属性
//无参构造
public Student() {
System.out.println("无参构造");
}
//构造
public Student(String name) {
System.out.println("构造器");
this.name = name;
}
//普通初始化块1
{
System.out.println("普通初始化块1");
}
//普通初始化块2
{
System.out.println("普通初始化块2");
}
//静态初始化块1
static {
System.out.println("静态初始化块1");
}
//静态初始化块2
static {
System.out.println("静态初始化块2");
}
}
new两个对象,观察执行顺序
public class Test {
public static void main(String[] args) {
Student student1=new Student();
System.out.println("--------------------------------------");
Student student2=new Student("构造器");
}
}
结果分析: 静态初始化块,只加载一次,谁先定义就先执行谁;由于是在加载期执行,所以最先执行 执行顺序是 静态初始化块>初始化块>构造方法
封
封代表的含义是:信息隐藏。
这里的“信息”是一个很大的概念,不仅仅是对与数据的隐藏,也包括算法的隐藏;隐藏也不是说完全不然外部看到,可以是能看不能动,或者能动不能看,或者只让某部分外部能看能动,灵活性很大。
—— 访问修饰符
所谓“封”就是信息的隐藏,这里的信息是一个很广泛的概念,不仅仅是指数据,也还指代了很多其它的东西,比如:算法,或者说一个功能的具体实现。
站在这个角度来说的话,先人给我们设计的那些我们可以直接调用的方法,其实都是封的体现。你想要一个随机数,你不用自己去实现它的具体步骤,先人已经把这个步骤写好并封在一个Math.random()方法里面了。它的具体实现对于我们来说是不可见的,我们也不需要见,只要拿到结果就可以了。 所以,封的概念其实是非常广泛的。只是我们作为学习Java的初学者而言,我们更多的关注点是把封看成我们装在类里面的内容是否能够对外部直接可见,也就是说可见性这件事情上。
在Java当中,提出了访问修饰符这样的一个概念,用来对装在类当中的内容对于外部可见性进行了限制。
一共三个关键字,4种情况:
- public --- 公共的 自己可访,外部任意可访
- protected --- 受保护的 自己可访问、子类可访问(子类自己内部才可访继承于父类的)、同包类可以访问
- 默认不写 --- 同包的 自己可访,同包可访
- private --- 私有的 自己可访
规范:
- 所有的变量属性都是private 的;
- 所有的常量属性都是public的;
- 构造方法除非是特殊的设计模式,否则都应该是public的;
- 普通方法根据该方法是否允许在本类之外被调用,选择使用public或private。
—— get/set方法
由于我们让所有的变量属性都设计为了private,所以外部是没有办法直接访问这个对象的任何一个变量属性了。咋办呢?
Java的解决方案是提供get/set方法。
1、get方法 --- 访问器
它是专门用来获取某个私有属性的值的。
语法: public 属性类型 get属性名(){
return this.属性;
}
这个书写方式是SUN公司规范的标准Java类的书写方式。
细节:
- 通常访问修饰符都是public;
- 用要获取的属性类型作为该方法的返回类型;
- 除了boolean类型的属性,其它属性对应的访问器名称都是get+属性名,属性名首字母改大写; boolean类型的属性,对应的访问器名称是is + 属性名,属性名首字母改大写。
- get方法通常没有参数,因为它的任务是从内部返回值到外部,而不是从外部接收值到内部。
2、set方法 --- 修改器
它是专门用来让外部对对象中的某个私有属性赋值的。
语法: public void set属性名(属性类型 属性名){
this.属性名 = 属性名; }
细节:
- 通常访问修饰符都是public;
- 通常的返回类型都是void的,因为set方法的任务是从外部传递数据到对象的内部,而不是从对象内部返回数据到外部;
- 属性对应的访问器名称都是set+属性名,属性名首字母改大写;
- set方法的参数通常就是属性变量的声明形式。
提问:既然需要对一个对象的属性在外部进行访问操作,那么为什么不直接把这个属性设计为public的,而是要书写更多的代码去实现get/set方法呢?
因为:get/set方法具有更大的灵活性
- 单独提供get或set可以让这个属性称为只读属性或只写属性。这是public做不到的。
- 对于set方法来说,我们还可以提供统一的数据有效性控制。如果是设计为public的属性,那么相当于把数据的有效性控制放弃掉了。
- 通过get/set方法,我们还可以隐藏属性在内存中的真正存放形