1.什么是封装?
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
eg:人关门,是门被关上,因此关门这个方法应当归属于门,人只是调用了门关门的方法。
不懂?
a持刀行凶,造成了b的死亡,b死亡的行为应当归属于b,a是调用了b死亡的方法。
正是这样,就应了上面那句对象代表什么,就得封装对应的数据,并提供数据对应的行为。
public class student{
String name;
String id; //定义成员变量
int age;
}
如果直接像上面一样定义类中的成员变量,违背了面向对象中的封装思想。
上面提到了封装要使细节部分包装、隐藏起来。这就不得不提到权限修饰符private
private:顾名思义,私有的,私有的东西只能在本类中使用,是属于一个权限修饰符,关于权限修饰符,还有protected、默认(也就是没有权限修饰符修饰)和public。它们的权限关系由大到小是:public>protected>默认>private。
public class student{
private String name;
private String id; //利用封装思想,将学生类中的成员变量包装,隐藏起来
private int age;
}
但是利用private修饰了学生类中的成员变量后,外面想要利用学生类创建对象的时候,无法对成员变量name、id、age进行获得和取值,因为成员变量被private修饰了,无法与外界取得联系,那该怎么办?
既然被private修饰的变量成员的权限被限制在本类中,无法与外界取得联系,我们是否可以通过一个媒介与外界取得联系呢,于是就衍生出了set和get方法。(这里的set和get方法的权限修饰符应当改为publc,不然外部无法进行修改或获得数值)
public class student{
private String name;
private String id; //因为student类中的成员变量被private权限修饰符修饰了,外面无法
private int age; //访问,所以就有了set(修改值)和get(获取值)方法
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setId(String id){
this.id=id;
}
public String getId(){
return id;
}
public void setAge(){
this.age=age;
}
public int getAge(){
return age;
}
}
综上之后,我们就可以利用student类创建对象,student stu=new student();
可能一会纳闷,我们书写的student类中并没有空参构造啊,为什么创建对象的时候不会报错呢?
那是因为,当我们没有书写构造方法的时候,虚拟机会自动帮我们加上一个空参构造,因此不会报错。但是有的时候我们希望在创建对象的同时就给对象赋值,而不是利用set方法,给每一个成员变量赋值时,我们就可以使用有参构造。空参构造和有参构造实际上都是为了在创建对象的时候进行初始化。创建对象时,虚拟机会自动调用构造方法,作用是给成员变量进行初始化的。
public class student{
private String name;
private String id; //因为student类中的成员变量被private权限修饰符修饰了,外面无法
private int age; //访问,所以就有了set(修改值)和get(获取值)方法
public student(){};
public student(String name,String id,int age){
this.name=name; //这里使用空参构造和有参构造时不能加上返
this.id=id; //回值,就连void都不能添加
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setId(String id){ //每个成员变量的set和get方法
this.id=id;
}
public String getId(){
return id;
}
public void setAge(){
this.age=age;
}
public int getAge(){
return age;
}
}
上图中的this的本质是所在方法调用者的地址值,作用是区分局部变量和成员变量。
1.1static关键字
当我们定义类的时候,发现多个类拥有相同的成员变量时,需要用到static关键字
eg:student stu1=new student(); //利用空参构造方法初始化对象
stu1.setName("张三");
stu1.setId("001");
student stu2=new student();
stu2.setName("李四");
stu2.setId("002");
假设有一个班级的学生,他们都是同一个老师。
当我们想给student类加一个老师名字的成员变量时,我们需要同时给张三对象中的老师名字和李四对象中的老师名字赋值,但是张三和李四共享一个老师的名字,也就是说所有的student类创建的对象中的老师名字成员变量应该都是相同的,但是我们每次利用student类创建对象时,都需要将老师名字这一成员变量进行重新赋值,会造成代码重复书写的情况,因此这个时候需要用到static关键字修饰。
1.1.1 static修饰成员变量和方法
1.被static修饰的成员变量,叫做静态变量
特点:①被该类所有对象共享
②不属于对象,属于类
③随着类的加载而加载,优先于对象存在
调用方式:①类名调用(推荐) eg:student teacherName="阿伟";
②对象名调用
①为什么推荐使用类名调用呢?
因为被static修饰的成员变量被所有对象共享(数据共享),并不独享于某一个对象,所以使用类名调用更为合适。
②怎么判断成员变量要不要使用static关键字修饰?
抓住核心,共享。
2.被static修饰的成员方法,叫做静态方法
当使用工具类的时候,可以将工具类中的方法用static修饰。
public class ArrayUtil{
private ArrayUtil(){}; //因为工具类创造对象是无意义的,所以工具类的空参构造权限设置
//为private
public static String average(int[] arr){ //工具类中的方法加上static修饰,表示工具共享
StringBuilder sb=new StringBuilder();
sb.append("[");
for(int i=0;i<arr.length;i++){
if(i==arr.length-1){
sb.append(arr[i]);
}else{
sb.append(arr[i]).append(",");
}
}
}
sb.append("]");
return sb.toString();
}
利用工具类创造对象是没有意义的,因此给工具类的初始化空参构造的权限修饰符设置为private。
工具类中的方法可以使用static修饰。
调用静态方法可以无需创建对象,之所以不允许静态方法访问实例成员变量,是因为实例成员变量是属于某个对象的,而静态方法在执行时,并不一定存在对象,静态方法属于整个类。
因为静态方法随着类的加载而加载,属于整个类。非静态方法随着对象的创建而加载。
所以,静态方法又叫类方法,实例方法又叫对象方法。
注意:1.静态方法中没有this关键字
why?在静态方法中没有this关键字因为静态是随着类的加载而加载,而this是随着对象的创建而存在的。静态比对象优先存在。
2.静态方法中,只能访问静态
why?静态的内容是随着类的加载而加载,所以当类加载时,静态方法和静态成员是先进入内存的,而此时的非静态成员变量是不在内存里的,因此静态方法找不到其他非静态成员。
3.非静态方法可以访问所有
why?因为静态方法随着类的加载而加载,而非静态的方法当产生对象时才加载,因此静态方法优先于非静态方法,在调用非静态方法时,无论静态还是非静态都已经加载完成,因此非静态可以访问所有。
2.什么是继承?
当我们定义一个学生和老师对象时,学生和老师共有的属性有姓名、年龄、睡觉等,那我们在定义两个对象时,需要创建两个类,相同的属性会重复书写。因此我们可以利用继承将相同的属性抽取到父类中,形成继承关系。
继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
特点:①Java只支持单继承,不支持多继承,但支持多层继承
②Java中所有的类都直接或间接地继承于Object类,即Object是最大的类
当不同一类中的方法相同时,可以考虑使用继承结构。
可是即使父类和子类产生继承关系,子类也不能继承父类的所有,那么子类能继承父类的哪些东西?
①子类无法继承父类的构造方法,无论是否用private权限修饰符修饰。why?
因为当父类写出构造方法时,因为构造方法的方法名与类的名字相同,如果子类继承了父类的构造方法时,就相当于在子类中拷贝了一份父类的构造方法,可是构造方法的名字是父类的,和子类的类名不同,无法使用,所以子类无法继承父类的构造方法,如果一定要使用,只能在子类中重新写一遍构造方法。
子类中所有的构造方法默认先访问父类中的无参构造,再执行自己。why?
1.子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
2.子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化。
子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行。
如果想调用父类有参构造,必须手动写super进行调用。
如果想要在子类中调用父类的空参构造和有参构造时,需要使用super关键字
②子类可以继承父类的成员变量,无论是否用private权限修饰符修饰,其中用private修饰的父类成员变量不能直接获取,需要使用与之相对应的set和get方法来获取。why?
父类中的成员变量可以被子类给继承,但是父类中被private修饰的成员变量不能直接使用,因为private修饰符是一个权限修饰符,被private修饰的成员变量只能在本类中使用,但是实际上,子类还是继承了父类中被private修饰的成员变量。
③子类可以继承父类中的虚方法表。why?
什么是虚方法表? 非private、非static、非final修饰的成员方法。
2.1方法的重写
当父类的方法不能满足子类现在的需求时,需要进行方法重写。
3.什么是多态?
同类型的对象,表现出的不同形态。
父类类型 对象名称=new 子类对象 Fu f=new Zi;(自动类型转换)
构成多态的前提:
①有继承关系
②有父类引用指向子类对象
③有方法重写
多态的好处:①定义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
②在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
多态的弊端:使用多态时,不能调用子类的特有方法。why?
因为当调用成员方法时,编译看左边,运行看右边,程序运行时会先检查父类中有没有这种方法,没有这种方法,程序会直接报错
如果想要使用,只能进行强制类型转换
eg: if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){ //当a对象是右边类的实例时,强制类型转换
c.catchMouse(); //成右边类的类型,并且将对象名转换成右边
}
当利用多态调用成员变量时,编译看左边,运行也看左边。why?
因为我们知道父类中的成员变量无论是否被private修饰,都会被子类给继承,因此编译看左边,运行同样可以看左边。
当利用多态调用成员方法时,编译看左边,运行看右边。why?
因为形成多态的前提是有方法重写,所以父类中的虚方法表被覆盖了,所以执行调用成员方法时,编译看左边,但运行看右边,因为此时子类中的虚方法表已经被重写了。