14. 面向对象的特性
一、封装
在面向对象的设计方法中,用于对代码细节进行包装和隐藏的方法。
可以将封装理解为一个保护措施,防止被外部类的代码随机访问。
访问代码的条件:只通过一个接口进行访问。
封装的功能:将程序需要执行的代码流程进行包装和隐藏,同时降低代码的耦合性,便于代码的日常维护和扩展。
封装的特点
-
提高了程序的安全性,保护数据
-
统一接口
-
隐藏代码的细节
-
提高代码的易维护性和扩展性
this 关键字
此操作可以访问当前类的属性和方法
this的用法:
这是在类当中的操作
将此类的操作调用并打印:
在主方法中,创建该类的对象后通过调用该类的方法输入实参后,便可有输出this操作后的结果
Private 关键字
与public关键字相对立,俗称属性私有。
与public不同的是,无法直接访问或修改该对象的属性。
Private是访问权限最窄的修饰符,禁止跨包访问。
语法:关键字后为变量的代码结构
例:
private String name;
当一个类当中存在private关键字,那么后面的成员属性将不能直接访问
若要进行访问就必须创建一个接口
创建接口的方法
创建set 和 get:
-
首先创建一个set方法 用于成员变量值的设定
2.再通过get方法 将设定的变量值返回给成员属性
设置了set方法和get方法后 在主方法中进行调用
-
使用接口
使用封装这一功能 使程序变得结构化 代码变得更容易理解 对于之后功能的扩充有一定的帮助。
二、继承
继承的实质指的是一个类获取到另一个类对象属性的方法,将类进行抽象化,并按照一定结构划分。
继承被用来对程序进行扩展,可以在原有的程序中继续编写新添加的代码。提高了工作效率,使代码结构分明。
继承的特性:
子类(派生类)继承父类(基类)的属性和方法,具备父类的所有特性。
单继承性:一个父类可以拥有多个子类,但每一层子类只能存在一个父类的关系。
extends 关键字
给类添加一个继承的关系
语法:
public class son extends parent(){};
son(子类)继承parent父类
注意事项:
-
默认情况下父类的成员属性和方法都可以被子类访问
-
被final修饰的类(终极类)不能被子类继承
-
创建子类对象时会创建父类对象先执行父类构造器再回到子类构造器
-
父类的私有属性和方法不能被子类直接访问
-
父类的构造方法不参与子类的继承,父类的构造方法是独立
-
父类中被final修饰的方法不能被重写
-
子类创建的上转型对象不能访问子类的属性和方法,会失去在子类中新添加的功能
权限修饰符四种表达方式
public 公共 表示最高权限 可以跨包跨类访问
Protected 受保护的 允许当前类和此类的子类访问,除了子类其他类不能跨包访问
Default 默认的 只能在当前包当前类访问,其子类和其他包不能访问
Private 私有的 权限最低,只能在当前类中访问
super 关键字
super关键字是用于指向父类对象的操作
注意事项:
此操作可以在子类方法中调用和访问其父类的属性和方法
用于在子类构造器中标识访问父类的构造器
访问父类属性和方法的用法:
1. 创建一个父类
在定义类的语句中,含有注释的这一段代码是默认执行的,因为所有的类都存在于Object类当中
-
为了方便表示子类和父类之间的关系,创建一个与Parent类有继承关系的类
在这个测试方法中 就实现了使用super关键字访问父类当中的相关数据
加载父类的构造方法:
-
显示父类的无参方法
-
显示子类的构造方法
-
用主方法创建子类的对象时
创建子类的对象之前 程序会提前加载类的构造器 如果此类与其他类具备继承关系 则会优先加载其父类的构造方法
但是加载的构造方法要么是无参数的构造,要么是有参的构造,两者只能取其中一个
super与this的区别:
-
访问的对象不同,super是访问其父类的构造方法、实例方法和属性
-
使用条件不同,super必须在有继承关系的类中使用
super的注意事项:
-
当创建含有继承特性的对象时,会将其父级的构造器一同加载
-
创建对象的构造器具有唯一性,要么调用自身类的构造器,要么调用父类的构造器
-
继承性,当其父类当中没有无参构造器,那么子类的无参构造将会无法创建
方法的重写
在满足继承关系的前提下,子类对父类方法函数进行修改或覆盖的操作。
满足重写的条件:
子类重写的函数与父级类的函数的结构相同(函数名 参数类型),函数内代码不相同
访问修饰符为private的方法不能被子类重写
子类重写的方法访问级别不低于其父类方法的访问级别
父类的方法被final修饰不能被重写
子类不能重写父类的静态方法,但是可以覆盖。覆盖方法:与重写方法不同,没有@Override注解,与父类同名的方法为两个完全独一的方法
重写方法的操作:
在两个类确定谁继承谁后,在子类中创建一个与父类相同名字相同类型的方法,结果则是子类覆盖了父类的方法
Man类继承People类
三、多态
不同类型的对象指向相同的类,在程序运行时实现不同功能的方法
多态相当于在同一接口中使用不同的实例化执行不同的操作
多态的用处:使程序具有可扩展性,可以实现不同对象的通用性
多态的优点:1. 可替换性 2. 可扩展性 3. 接口性 4. 灵活性
多态的前提条件:
-
继承
-
子类重写父类
-
父类的引用指向子类
由于多态提高了创建对象的灵活性,可以使用不同的引用类型
创建对象的格式:
Man boy = new Man();
左边的子类的引用类型指向子类,也可以为有关联类(父类)的引用
右边的实际类型是确定的,一般指向是哪个类的
在Man类继承People类的案例中:
可以看出虽然创建的对象不同,但是结果都是子类的
原因:由于子类重写了父类的方法,不管是父类的引用还是子类的引用使用的都是被子类覆盖的方法。若使用的方法是各自独有的,则会导致结果的不同
多态的注意事项:
-
重写的方法仅限于非静态方法,因为静态方法调用是哪个类就是用哪个类的方法
-
在多态中子类可以调用自己的方法,重写父类的方法
-
父类不能直接调用子类独有的方法
instanceof 关键字
用于判断对象是否属于这个类的实例。结果为布尔表达式,属于为true,不属于为false
判断公式:a instanceof A
a 对象 A 类
使用instanceof的判断条件:
首先a对象的类与A类是否有继承关系,如果没有关系,则编译报错
实例体现:创建一个父类Person、两个子类Student和Teacher
-
根据instanceof的判断条件可以得知:
若当前判断对象的从属类跟判断的类一样则表示为true,若当前判断对象的从属类跟判断的类有直接的继承关系,也属于该对象的实例所以结果为true
-
根据对象所指向的实际类型判断:
若对象的引用类型指向的类不是表达式中的类,则判断为false
-
根据继承特性判断:
若对象的类与判断的类没有继承关系,程序在运行时会编译错误
引用类型转换
与基本数据类型的转换方式相同,把子类看成是低一级的类,父类看成是高一级的类。若要把高一级类的对象转换成低一级则需要强制转换,低转高为自动转换
创建父类Person和子类Student的案例:
父类拥有Per方法,子类拥有Stu方法
高转低
由于创建的student对象类型为父类,要强制转换成子类的引用类型才能使用子类的方法
低转高
对象类型为子类,自动转换为高一级的引用类型可以直接访问父类的方法。但是会失去对象转换前子类方法的访问的权限
四、抽象类
在面向对象的概念里,所有的对象都是通过类来描述的。如果反过来,并不是所有的类都可以创建对象的。如果一个类中没有用来描述对象的内容就是抽象类。
抽象类虽不能创建对象,但是具备普通类的其他功能,可以包含成员变量、成员方法和构造器。
特性:
抽象类的方法必须被继承才能拿来使用,一个类只能继承一个抽象类。
抽象类自身不能实例化,必须通过其子类进行实例化
抽象类不能被final修饰
abstract修饰符
Java当中使用abstract修饰符来定义抽象类
public abstract class Abstract Demo{};
抽象类的定义
抽象类可以包含自己的属性、方法和构造器,但不能通过自身的对象拿来使用
抽象方法
abstract不仅可以修饰类,还可以修饰方法。
注意点:
-
抽象方法不能被定义,所以需要通过子类重写才能进行定义
-
若一个类中包含了抽象方法,那么这个类也必须定义为抽象类
子类继承抽象类
继承抽象类的前提:
-
重写抽象类的抽象方法,继承抽象类的子类中必须有对抽象类中抽象方法的实现
-
这个类也声明为抽象类
若继承抽象类的子类也声明为抽象的,那么该类也不能创建对象
抽象类对象上转型对象,实例是子类的,对象类型是抽象的,因此抽象类对象也会失去在子类中的一些功能
抽象类继承抽象类
当抽象类的继承对象也是抽象类,其抽象子类也包含抽象类的所有特性,除了可以定义抽象方法和非抽象方法,也可以重写抽象父类的抽象方法,以便于相关子类不用强制重写抽象类
适配器引用
当抽象类中有多个抽象方法,若要被子类继承,就必须全部重写
为了让子类只需要继承个别抽象方法,就需要通过一个适配器类来对重写的抽象方法进行空实现
创建一个抽象类
package com.JavaSE.Abstract; public abstract class Student { /* 创建多个抽象方法 */ public abstract void study(); public abstract void makeMoney(); public abstract void work(); public abstract void getMarry(); }
创建一个适配器类
作为抽象类的一个适配器是一个普通的类,适配器类继承抽象类
并对其子类实现的抽象方法进行空实现(方法体中不用写任何信息)使得继承抽象类的子类不需要依次重写不需要的抽象方法。
就像电脑的适配器一样,适配器一头继承抽象方法,另一头继承抽象的功能子类
public class StudentAdapter extends Student{ @Override public void study() { //空实现 代替实现功能的子类进行重写 } @Override public void makeMoney() { } @Override public void work(){ } @Override public void getMarry() { } }
继承抽象类的子类
import com.JavaSE.Abstract.StudentAdapter; //使用适配器类需要继承 public class Man extends StudentAdapter { //子类继承适配器后可以只重写业务中需要的抽象方法 @Override public void study(){ System.out.println("一个家庭顶梁柱需要有足够的知识储备"); } }
五、接口
接口是一个Java抽象类型的集合。
一个类继承了一个接口,从而来继承这个接口中的所有抽象的方法,使Java具有多继承性。
接口是用来被实现的
接口的特性:
-
被接口定义的属性为常量,不得修改。隐式指定为 public static final
-
接口定义的方法为隐式抽象的,不能写入其内容。隐式指定为 public abstract
-
接口可以声明方法,但不能实现
-
接口定义的抽象方法和常量只能是public 或缺省这两种访问修饰符
定义接口并实现
interface关键字
接口不是一个类,不能写入方法的内容。
接口定义变量
-
在接口中可以声明变量,变量的类型可以是各种类型。
-
必须是public static final所修饰
-
灰色部分可以忽略不写
接口定义方法
-
接口跟普通类一样,可以定义多个方法,但是方法的类型必须是抽象的。
-
public abstract所修饰
在jdk1.8版本的新特性:接口支持使用default权限的方法
default void ran(){};
实现接口方法
implement关键字让一个类实现这个接口
实现前提:1. 必须重写接口的所有方法。2. 该类也是被abstract修饰符修饰
需要注意的是:若该类实现了接口,其性质也转变为抽象类,不能被直接实例化
接口的继承
子类接口对父类接口中的抽象方法和常量进行继承,与父类和子类一样,除了继承父类的所有功能之外,子类接口也可以定义属于自己的特殊实例方法或常量
接口中常量和静态方法的创建
public interface interfaceImp{
//接口java系统默认只能定义静态常量 常量的修饰符只能是public abstract可以省略
String feature = "接口的特征";
//接口只能定义抽象方法,接口只能被实现或被继承 public static final可以省略
void absMethod();
}
子类对接口的继承 子类必须是个接口 继承父类接口的方法
//继承接口的类型必须是接口
public interface InterfaceImp extends Interfaces {
//可以重写父类接口中的抽象方法 但是修饰符为default
@Override
default void absMethod() {
System.out.println("这是重写接口的抽象方法");
}
}
对接口实现的子类
//通过implements实现类来访问接口的属性
public class Son implements InterfaceImp {
@Override
public void absMethod(){
System.out.println("修饰符为public对接口方法的实现");
}
public void realization(){
System.out.println(feature);//打印常量属性
absMethod();//调用方法
}
}
总结
接口和普通类的相同点:
-
接口可以被子类接口继承
接口和普通类的不同点:
-
接口不能实例化
-
接口没有构造方法
-
接口中定义的方法都是抽象类型的
-
接口不是被继承,而是被实现
-
接口具有多继承性,一个类可以实现多个接口
六、内部类
在Java中,还存在一些比较特殊的创建类的方法,类似于类当中的嵌套操作。
内部类局限于外部类,只在外部类之中被访问
根据不同的嵌套操作进行分类
成员内部类
静态内部类
局部内部类
匿名内部类
内部类的特点:
-
内部类可以使用public protected private 缺省,四种访问修饰符
-
内部类可以定义成abstract final static
-
内部类没有Java源文件,只有class字节码文件
成员内部类
在A类中再定义另外一个B类,A类为B类的外部类,B类为A类的内部类
案例:
成员内部类跟普通类的共同点,可以创建对象。
对于内部类方法调用与外部类的区别:需要通过外部类的对象进行内部类的实例化。
通过外部类对象创建内部类的构造方法并赋值给Outclass.Inclass引用类型的内部类对象in
内部类的特性:内部类可以访问外部类的私有属性或方法
访问私有属性的案例:
静态内部类
可以用public static关键字修饰的类称为静态内部类
创建条件:必须在一个类之中定义
案例:
静态内部类中内部类可以直接调用外部类的静态属性和方法,但不能调用内部类的非静态变量和方法
静态内部类注意事项:在静态内部类中不可以定义抽象方法
局部内部类
和局部变量特性相似,作用域只限于方法当中的类,因此只能在这个方法当中被使用
案例:
局部内部类中不可以定义静态变量和属性,所有的变量和属性这个方法之外不能被其他类调用
匿名内部类
不需要定义对象的名字就可以实现调用该类属性和方法的操作,可以更方便而简洁的去访问类的属性或方法,而不用创建太多对象类型
匿名内部类的特征
-
匿名类的对象没有对象类型
-
匿名类经常用于接口或抽象方法的实现,可以减少接口和抽象类实现类的定义
-
匿名类属于它实现的父级类型
案例:
七、Static关键字解析
static关键字可以用来修饰类、成员变量和成员方法。
被static修饰的成员变量称为静态变量。
用来修饰方法则称为静态方法
静态属性、静态方法、非静态属性和非静态方法的各使用方法
静态代码块中内容的定义规范
-
静态代码块只能写在类当中
-
静态代码块中可以访问类中的静态变量和静态常量
-
静态代码块中可以定义局部变量和局部常量
-
可以定义局部类
-
不可以访问或定义实例变量和方法,但是可以在局部类当中定义
代码演示
public class DemoClass {
public static String average;//声明静态变量和常量
public static final String CONSTANT;
static{
//给变量和常量赋值
average = "这是静态变量";
CONSTANT = "这是静态常量";
//定义局部变量和常量
String local = "这是局部变量";
String LOCAL_CONSTANT = "这是局部常量";
//定义一个局部类
class LocalClass{
String localAverage = "这是局部类变量";
String localMethod(){
return "这是局部类的方法";
}
}
//访问局部类中的变量和方法
System.out.println(new LocalClass().localAverage);
System.out.println(new LocalClass().localMethod());
}
}
静态代码块的内存运行机制
静态代码块与对象无关,所以不能在静态代码块中出现this或super与对象相关的关键字,并且静态块跟类同时加载,所以静态代码块中的内容不管调用多少次也只能执行一次,并且比所有构造器加载得早
在类产生之前写入一些初始化参数、对静态变量和常量进行赋值,以及调用静态方法
静态导入包的操作