⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐
🌀 个人首页:🏠 星空之路Star 🏠
🌀 所属专栏:📖 Java知识点总结 📖
🌀 大家好🤝 我是 👉老孙👈
🌀 喜欢用博客记录人生 ✍️
🌀 未来学习路上请多多关照 🙏
🌀 星光不问赶路人, 时光不负有心人🕣
⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐ ⭐
面向对象目录:
- 一、面向对象概述
- 1、面向过程和面向对象
- 2、面向对象的三大特征
- 3、面向对象的思想概述
- 二、类和对象创建和使用
- 三、封装
- 1、四种访问权限修饰符
- 2、封装性的体现
- 四、继承
- 五、多态
- 1、多态性概述
- 2、instanceof操作符
- 3、类型转换
- 六、类的五大内部结构
- 1、属性(Field)
- 2、方法(Method)
- 3、构造器/构造方法(Constructor)
- 4、代码块(Code Block)
- 5、内部类(Inner Class)
- 七、方法的重载和重写
- 1、重载 (Overload)
- 2、重写 (Override)
- 八、抽象类
- 九、接口
- 1、接口的概念
- 2、接口的特点和语法
- 3、接口的应用:代理模式
- 4、接口的应用:工厂模式
- 5、接口和抽象类之间的对比
一、面向对象概述
1、面向过程和面向对象
面向过程(POP) 与 面向对象(OOP)对比:
- 二者都是一种思想,面向对象是相对于面向过程而言的。
- 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。
- 面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
- 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
典型例子:人把大象装进冰箱?
2、面向对象的三大特征
- 封装性 (Encapsulation)
- 一层含义是指把对象的属性和行为看成是一个密不可分的整体,将这两者 “封装” 在一起,即封装在对象中。
- 另一层含义是指 “信息隐藏”,将不想让外界知道的信息隐藏起来。
- 例如,驾校的学员学开车,只需要知道如何操作汽车,无需知道汽车内部是如何工作的。
- 继承性 (Inheritance)
- 继承性主要描述类与类之间的关系,通过继承,可以在无需重新编写原有类的情况下,对原有类的功能进行扩展。
- 继承不仅增强了代码的复用性,提高了开发效率,还降低了程序产生错误的可能性,有利于程序的维护和扩展。
- 多态性 (Polymorphism)
- 多态性是指在一个类中定义的属性和方法被其他类继承之后,他们可以具有不同的数据类型或表现出不同的行为,这使得同一个属性和方法在不同的类中具有不同的语义。
3、面向对象的思想概述
- 程序员从面向过程的 执行者 转化成了面向对象的 指挥者
- 面向对象分析方法分析问题的思路和步骤:
- 根据问题需要,选择问题所针对的 现实世界中的实体。
- 从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了 概念世界中的类。
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。
- 将 类实例化成计算机世界中的对象,对象是计算机世界中解决问题的最终工具。
二、类和对象创建和使用
类(Class)和 对象(Object)是面向对象的核心概念:
- 类是对一类事物的描述,是抽象的、概念上的定义。
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance),万事万物皆对象。
- 例如:类 = 抽象概念的人、对象 = 实实在在的某个人
创建对象的前提必须先定义一个类,类是对象的抽象。
- 面向对象程序设计的重点是 类的设计。
- 类的设计,其实就是类的成员的设计。
//一、 设计类,其实就是设计类的成员
属性 = 成员变量 = field = 域、字段
方法 = 成员方法 = method = 函数
创建类的对象 = 类的实例化 = 实例化
//二、 类和对象的使用(面向对象思想落地的实现)
第一步:创建类,设计类的成员(类的成员:方法和属性)
第二步:创建类的对象
第三步:通过 “对象.属性” 或 “对象.方法” 调用对象的结构
//三、 如果创建了一个类的多个对象,则每一个对象都独立拥有一套类的属性(非static的)
意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值
类和对象的创建和使用实例:
// 第一步:创建类,设计类的成员(类的成员:方法和属性)
public class Person {
// 属性:name、age
String name; // 姓名
int age; // 年龄
boolean isMale; // 是否是男性
// 方法
public void eat() {
System.out.println("人可以吃饭");
}
public void sleep() {
System.out.println("人可以睡觉");
}
public void talk(String language) {
System.out.println("人可以说话,使用的是:" + language);
}
}
public class OOPTest1 {
public static void main(String[] args) {
// 第二步:创建Person类的对象
Person p1 = new Person();
// 第三步:调用对象的结构:属性、方法
// 1、调用属性: 【对象.属性】
p1.name = "Tom";
p1.age = 10;
System.out.println(p1.name); // Tom
System.out.println(p1.age); // 10
// 2、调用方法:【对象.方法】
p1.eat(); // 人可以吃饭
p1.sleep(); // 人可以睡觉
p1.talk("Chinese"); // 人可以说话,使用的是:Chinese
//----------------------------------
// 将p1变量保存的对象地址赋值给p2,导致p1和p2指向了堆空间中的同一个对象实体
Person p2 = p1;
System.out.println(p1.name); // Tom
p2.age = 20;
System.out.println(p1.age); // 20
}
}
对象的内存解析
三、封装
封装性:把一个类该隐藏的信息隐藏起来,该暴露信息的暴露出来。
1、四种访问权限修饰符
Java权限修饰符public、protected、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
对于 class 的权限修饰符只可以用 public 和 default(缺省)
- public类 可以在任意地方被访问
- default类 只可以被同一个包内部的类访问
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
注:Yes表示能访问到
2、封装性的体现
使用权限修饰符修饰类及类的内部结构:属性、方法、构造器、内部类
-
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
-
修饰类的话,只能使用:缺省、public
-
问题的引入:当我们创建一个类的对象以后,我们可以通过 对象.属性 的方式,给对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能再属性声明时体现,我们只能通过方法进行限制条件的添加。(比如通过setName()限制名字不能为空,setAge()限制年龄的范围是1~120等等,同时,我们需要避免用户再使用 对象.属性的方式对属性进行赋值。则需要将属性声明为私有的(private).
-
封装性的体现:
- 权限修饰符:我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx) 和 设置(setXxx)此属性的值。
- 扩展:封装性的体现:① 如上 ② 不对外暴露的私有的方法 ③ 单列模式
简单举例:
public class Person {
// 属性私有化
private String name;
private String age;
public Person() {
}
public Person(String name, String age) {
this.name = name;
this.age = age;
}
// 提供外界访问属性的公告Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public class PersonTest {
public static void main(String[] args) {
Person person = new Person("张三", "18");
System.out.println(person.getName() + "现在的年龄是:" + person.getAge());
person.setAge("66");
System.out.println(person.getName() + "现在的年龄是:" + person.getAge());
}
}
四、继承
继承性:就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,使得子类具有父类相同的行为。
Java关于继承性的规定:
- 一个类可以被多个子类继承,一个类只能有一个父类(Java中类的单继承性)
- 子父类是相对的概念
- 子类直接继承的父类称为:直接父类;间接继承的父类称为:间接父类
- 子类继承父类以后,就获取了父类以及所有间接父类中声明的属性和方法。
继承性的好处:减少了代码的冗余,提高了代码的复用性;便于功能的扩展;为之后多态性的使用,提供了前提。
Java只支持单继承和多层继承,不允许多重继承。
继承性的语法格式:
// A:子类、派生类
// B:父类、超类、基类
// extends:延展、扩展
class A extends B {}
体现:一旦子类A继承父类B以后,子类中就获取了父类B中声明的所有的属性和方法。特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。子类继承了父类以后,还可以声明自己特有的属性和方法,实现功能的扩展。子类和父类的关系,不同于自己和集合的关系。
简单举例:父类Person、子类Student
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends Person{
private String school;
public Student() { }
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
public String getSchool() {
return school;
}
public void studentInfo(){
System.out.println("姓名:" + getName() + "、年龄:" + getAge() + "岁、学校:" + getSchool());
}
}
public class StudentTest {
public static void main(String[] args) {
Student student = new Student("张三", 10, "希望小学");
student.studentInfo();
}
}
五、多态
1、多态性概述
多态性(Polymorphism):对象多种表现形式的体现
- 对象的多态:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
- 可以直接应用在抽象类和接口上
Java引用变量有两个类型:编译时类型 和 运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
- 若编译时类型和运行时类型不一致,就出现了对象的多态。
- 多态情况下:
- 看左边:看的是父类的引用(父类中不具备子类特有的方法)
- 看右边:看的是子类的对象(实际运行的是子类重写父类的方法)
对象的多态:在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
Person p = new Student();
Object o = new Person(); // Object类型的变量o,指向Person类型的对象
o = new Student(); // Object类型的变量o,指向Student类型的对象
- 向上转型:子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象。
- 多态的使用:虚拟方法调用
- 有了对象的多态以后,我们在编译期,只能调用父类中声明的方法,但在运行期间,我们实际
- 执行的是子类重写父类的方法。
- 总结:编译,看左边; 运行,看右边。
- 多态的使用前提:① 类的继承关系 ② 方法的重写
- 对象的多态,只适用于方法,不适用于属性(因为属性的多态编译和运行都看左边)
public class Person {
public String personMethod(){
return "personMethod...";
}
}
public class Student extends Person {
public String studentMethod(){
return "studentMethod...";
}
}
public static void main(String[] args) {
Person person = new Student();
System.out.println(person.personMethod());
// 只能调用父类的非private方法
Student student1 = (Student) person;
// 可以调用父类和子类的非private方法
System.out.println(student1.studentMethod());
System.out.println(student1.personMethod());
}
2、instanceof操作符
instanceof 操作符
- 语法格式:A instanceof B
- 作用:判断它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
- instanceof 是 Java 中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。
public class Person { .... }
public class Student extends Person { .... }
public class Teacher extends Person { .... }
--------------------------------------------------
public void method(Person e) {
if (e instanceof Person)
// 处理Person类及其子类对象
if (e instanceof Student)
//处理Student类及其子类对象
if (e instanceof Teacher)
//处理Graduate类及其子类对象
}
3、类型转换
基本数据类型的转换(Casting):
自动类型转换:小的数据类型可以自动转换为大的数据类型
long n = 888;
double d = 66.6f
强制类型转换:可以把大的数据类型强制转换成小的数据类型
注意:强制转换可能会出现精度缺失(基本语法的运算规则中已详细说明 运算符和运算规则)
float f = (float)66.6;
int n = (int)1200L;
Java引用类型转换:
- 从子类到父类的类型转换可以自动进行
- Java对象的强制类型转换称为造型
- 从父类到子类的类型转换必须通过强制类型转换实现
- 无继承关系的引用类型间的转换是非法的
- 在造型前可以使用 instanceof 操作符测试一个对象的类型
简单举例:父类Person、子类Student和Teacher
public class Person { .... }
public class Student extends Person { .... }
public class Teacher extends Person { .... }
public class Person {
private String name;
private int age;
public Person() {}
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Student extends Person{
private String school;
public Student() {
}
public Student(int age, String name, String school) {
super(age, name);
this.school = school;
}
public String getSchool() {
return school;
}
}
public class Teacher extends Person{
private String school;
public Teacher() {
}
public Teacher(int age, String name, String school) {
super(age, name);
this.school = school;
}
public String getSchool() {
return school;
}
}
public class Test {
public static void main(String[] args){
Test t = new Test();
Student student = new Student(18,"张三","XXX");
t.method(student);
Teacher teacher = new Teacher(36,"李华","XXX");
t.method(teacher);
}
public void method(Person e) { // 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法、编译时错误
if (e instanceof Student) {
Student s = (Student) e; // 将e强制转换为Student类型
System.out.println(s.getName()+" "+s.getAge()+"岁 "+s.getSchool()); //张三 18岁 XXX
}
if(e instanceof Teacher){
Teacher t = (Teacher) e;
System.out.println(t.getName()+" "+t.getAge()+"岁 "+t.getSchool()); //李华 36岁 XXX
}
}
}
六、类的五大内部结构
类的内部结构有五种:属性、方法、构造器、代码块 和 内部类
public class Person {
// 1、属性(或成员变量)
private String name;
private int age;
// 2、方法(或成员方法、或函数)
public void walk(){
System.out.println("人走路...");
}
public String info(){
return "姓名:"+name+"\n:年龄"+age;
}
// 3、构造器(或构造方法)
public Person() { }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
...
// 4、代码块
{
name = "StarSkyRoad";
age = 66;
}
// 5、内部类
class node{
int value;
node next;
}
}
1、属性(Field)
属性 (field) = 成员变量 = 域、字段
属性( 成员变量)与局部变量对比
1️⃣ 相同点:
- 定义变量的格式: 数据类型 变量名 = 变量值;
- 先声明,后使用(拥有对应的作用域)
2️⃣ 不同点:
- 在类中声明的位置不同:
- 属性:直接定义在类的一对 {} 内
- 局部变量:声明在方法内、方法形参、代码块、构造器、构造器内部的变量
- 关于权限修饰符的不同:
- 属性:可以在声明属性时,使用权限修饰符指明其权限
- 访问权限修饰符:private、public、缺省、proteted
- 其它权限修饰符:
- static:静态属性,被所有对象共享,可以直接用类名调用。
- final:常量,一旦初始化后不可修改。
- 局部变量:不可以使用权限修饰符
- 属性:可以在声明属性时,使用权限修饰符指明其权限
- 默认初始化值的情况:
- 属性:类的属性,根据其类型,都有默认初始化值
- 局部变量:没有默认初始化值
- 意味着,我们在调用局部变量之前,一定要提前赋值;特别是,形参在调用时,我们赋值即可。
- 在内存中加载的位置:
- 成员变量(属性):加载到堆空间中(非static)
- 局部变量 :加载到栈空间
2、方法(Method)
方法 (method) = 成员方法 = 函数
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。
- 将功能封装为方法的目的是,可以实现代码重用,简化代码。
- Java里的方法不能独立存在,所有的方法必须定义在类里。
方法的定义格式:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
// 一、权限修饰符
1、访问权限修饰符:public、protected、缺省、private
2、其它权限修饰符:static、final、abstract
2.1 静态方法:static 修饰的方法,在不创建对象的情况下,通过类名直接调用。
2.2 最终方法:final 修饰的方法,不能被继承和修改。
2.3 抽象方法:abstract 修饰的方法,无方法体,存在于抽象类和接口中
3、递归方法:在方法A中有调用了方法A
// 二、返回值类型:有返回值 VS 无返回值
1、如果方法有返回值,则必须在方法声明时,指定返回值类型。
使用 return 关键字 来返回指定类型的变量或常量。
格式:return 数据;
2、如果方法没有返回值,则声明时,使用void来表示。
通常,没有返回值的方法中,就不需要使用return。
但是,如果使用的话,只能用 return; 表示结束此方法的意思。
3、问题:我们定义方法时,该不该有返回值?
答:① 根据题目要求 ② 凭经验,具体问题具体分析
// 三、方法名: 属于标识符,遵循标识符的规则和规范,“见名知意” 。
// 四、形参列表:方法可以声明0个,1个,或多个形参。
1、格式:数据类型1 形参1,数据结构2 形参2, ...
2、问题:我们定义方法时,该不该定义形参?
答:① 根据题目要求 ② 凭经验,具体问题具体分析
// 五、方法体:方法功能的体现
1、在方法体内容可以调用当前类的属性和方法,静态方法只可以静态成员。
2、return关键字的使用:
2.1 使用范围:使用在方法体中
2.2. 作用:① 结束方法 ② 针对有返回值类型的方法,使用 return 数据; 方法返回所要的数据。
2.3. 注意点:return关键字后面不可以声明执行语句。
注意:方法不能嵌套定义
方法的分类:按照是否有形参及返回值
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名 () {} | 返回值的类型 方法名 () {} |
有形参 | void 方法名 (形参列表) {} | 返回值的类型 方法名 (形参列表) {} |
public void eat(){} // 无参 无返
public void sleep(int four){} // 有参 无返
public String getName(){} // 无参 有返
public String getNation(String nation){} // 有参 有返
方法的调用:
- 普通方法:实例对象名.方法
- 静态方法:实例对象名.方法 或 类名.方法
方法通过方法名被调用,且只有被调用才会执行。
3、构造器/构造方法(Constructor)
构造器的特征
- 具有与类相同的名称
- 不声明返回值类型,不能被static、final、synchronized、abstract、native修饰
构造器的作用:创建对象、初始化对象的信息
- 如:Order o = new Order(); Person p = new Person(“Peter”,15);
- 如同我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造器中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们要“洗澡”了。
构造器说明:
- 如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器
- 定义构造器的格式:权限修饰符 类名(形参列表){}
- 一个类中可以定义的多个构造器,彼此构成重载(一个类中,至少会有一个构造器)
- 一旦我们显示的定义了类的构造器之后,系统就不会再提供默认的空参构造器
public class Person{
private String name;
private int age;
// 构造器
public Person() {
System.out.println("Person()...");
}
public Person(String n) {
this.name = n;
}
public Person(String n, int a) {
name = n;
age = a;
}
// 方法
public void eat() {
System.out.println("人吃饭");
}
public void study() {
System.out.println("人可以学习");
}
}
public class PersonTest {
public static void main(String[] args) {
// 构造器的作用1:创建人的对象:new + 构造器
Person p = new Person();
p.eat();
// 构造器的作用2:初始化对象的信息【Tom】
Person p1 = new Person("Tom");
System.out.println(p1.name);
}
}
4、代码块(Code Block)
代码块(或初始化块):代码块的作用是用来初始化类、对象;代码块如果有修饰的话,只能使用static。
分类:静态代码块 和 非静态代码块
1、静态代码块:
内部可以有输出语句
随着类的加载而执行,而且只执行一次
作用:初始化类的信息
如果一个类中定义了多个静态代码块,则按声明的先后顺序执行
静态代码块的执行要优先于非静态代码块的执行
静态代码块只能调用静态的属性、静态的方法,不能调用非静态的结构
2、非静态代码块:
内部可以有输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块,则按声明的先后顺序执行
非静态代码块内可以调用非静态的属性、非静态的方法,或静态的属性、静态的方法
对属性可以赋值的位置
① 默认初始化
② 显式初始化 / ⑤ 在代码块中赋值
③ 构造器中初始化
④ 有了对象以后,可以通过【对象.属性】【对象.方法】的方式进行赋值
执行的先后顺序: ① -- ② / ⑤ -- ③ -- ④
// static代码块通常用于初始化static的属性
public class Person {
public static int total;
static {
total = 100;
System.out.println("in static block!");
}
}
public class PersonTest {
public static void main(String[] args) {
System.out.println("total = " + Person1.total);
System.out.println("total = " + Person1.total);
}
}
------------------------------------------------------
结果:
in static block
total=100
total=100
5、内部类(Inner Class)
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。
- 在Java中允许将一个类的声明位于另一个类的内部,前者称为内部类,后者称为外部类。
- Java中允许将第一个类A【?】在另一个类B中,则类A就是内部类,类B称为外部类
- Inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
- Inner class 的名字不能与包含它的外部类类名相同;
内部类的分类:
- 成员内部类:又分为静态内部类和非静态内部类
- 一方面,作为外部类的成员:
- 调用外部类的结构、
- 可以被static修饰
- 可以被4种不同的权限修饰
- 另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被final修饰,表示此类不能被继承。言外之意,不能用final,就可以被继承
- 可以被abstract修饰
- 一方面,作为外部类的成员:
- 局部内部类:可以位于方法内、代码块内、构造器内,不谈修饰符(匿名内部类)
七、方法的重载和重写
1、重载 (Overload)
- 重载的概念:在同一个类中,允许存在一个及以上的同名方法,只要它们的参数个数或者参数类型不同即可构成重载。
- 重载的特点:与返回值类型、修饰符、形参变量名、方法体无关,只参数列表有关(参数个数或参数类型),调用时,根据方法参数列表的不同来区别。
// 下面5个方法构成重载
int add(int x,int y) { return x+y; }
long add(int x,long y) { return x+y; }
long add(long x,int y) { return x+y; }
int add(int x,int y,int z){return x+y+z;}
double add(double x,double y){return x+y;}
// 与上面的方法都不构成重载
// void add(int x,int y){System.out.println("OK");}
// 常用类的方法构成重载:
Arrays类中重载的sort()、 binarySearch()
2、重写 (Override)
- 定义:在子类中可以根据需要对从父类中继承来的方法进行改造(也称为重置/覆盖),在程序执行时,子类的方法将覆盖父类的方法。
- 要求:
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 子类不能重写父类中声明为private权限的方法,且子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 子类方法抛出的异常不能大于父类被重写方法的异常
- 注意:子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
简单举例:子类Student 重写 父类Person的 getInfo() 方法
public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}
public class Student extends Person {
public String school;
public String getInfo() { // 重写方法
return "Name: "+ name + "\nage: "+ age + "\nschool: "+ school;
}
public static void main(String args[]){
Student s1 = new Student();
s1.name = "Bob";
s1.age = 20;
s1.school = "s1_school";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:s1_school
}
}
八、抽象类
抽象类:
- 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。
- 用abstract关键字来修饰类。
- 如果一个抽象类的所有方法都是抽象的,则可以将这个类定义接口。
抽象方法:
- 用abstract来修饰方法,只有方法的声明,没有方法的实现,以分号结束。
- 含有抽象方法的类必须被声明为抽象类。
- 抽象类不能被实例化。
- 抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 不能用abstract修饰变量、代码块、构造器。
- 不能用abstract修饰私有方法、静态方法、final的方法、final的类。
抽象类的定义格式:
abstract class 抽象类的名称{
属性;
访问权限 返回值类型 方法名称(参数){ //普通方法
return [返回值];
}
访问权限 abstract 返回值类型 抽象方法名称(参数); //抽象方法,无方法体
}
抽象方法的定义格式:
abstract 返回值类型 方法名称(参数);
九、接口
1、接口的概念
接口(interface)定义:接口和类是并列的两个结构、接口是对类的一组需求描述,这些类要遵循从接口的统一格式进行定义。(可以将接口看成没有实例域的抽象类)
- 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
- 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都支持USB连接。
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
- 接口的本质是契约,标准,规范,就像我们的法律一样,制定好后大家都要遵守。
- 接口是抽象方法和常量值定义的集合。
2、接口的特点和语法
接口的特点:
-
需要用 interface 来定义
-
接口中的成员:
- 接口中的所有 成员变量只能是常量(全局常量) 默认是由 public static final修饰(可省略)
- 接口中的所有 抽象方法 默认是由 public abstract修饰(可省略)
- Java8中接口新增功能:可以声明 默认方法 和 静态方法
- 默认方法:包含具有具体实现的方法,默认方法使用 default 关键字修饰 。
- 静态方法:用 static 关键字修饰的方法,使用方式 接口名.方法名
-
接口中没有构造器 (不可以实例化)
-
接口采用 多继承机制,接口与接口之间可以多继承。接口的具体使用,体现多态性。
-
接口的主要用途就是 被实现类实现。(面向接口编程)
- Java类可以实现多个接口 ,弥补了Java单继承性的局限性。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化;否则,仍为抽象类。
- 定义Java类的语法格式:先写extends,后写 implements 接口的语法格式:
-
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前),而没有变量和方法的实现。
接口定义的语法格式:
public interface 接口名 extends 接口1,接口2...{
public static final 数据类型 常量名 = 常量值;
public abstract 返回值类型 抽象方法名称(参数列表);
}
public interface Runner {
// public static final 可以省略不写
public static final int ID = 1;
// public abstract 可以省略不写
public abstract void start();
public abstract void run();
public abstract void stop();
}
定义一个接口的实现类的语法格式:
//一个类可以继承一个父类 和 实现多个接口
public class 类A extends 类B implements 接口A,接口B{
//实现 接口A 接口B所有的抽象方法
}
implements 和 interface 关键字对比
关键字 | 作用 |
---|---|
implements | 定义一个类实现的接口 |
interface | 一种抽象类型,其中包含可以由类实现的方法 |
实例:
interface Runner {
public void start();
public void run();
public void stop();
}
class Person implements Runner {
public void start() {
// 准备工作:弯腰、蹬腿、咬牙、瞪眼
// 开跑
}
public void run() {
// 摆动手臂
// 维持直线方向
}
public void stop() {
// 减速直至停止、喝水。
}
}
3、接口的应用:代理模式
代理模式(Proxy):是Java开发中使用较多的一种 设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
应用场景:比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
- 安全代理:屏蔽对真实角色的直接访问。
- 远程代理:通过代理类处理远程方法调用(RMI)
- 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
分类:(JDK自带的动态代理,需要反射等知识)
- 静态代理(静态定义代理类)
- 动态代理(动态生成代理类)
// 静态代理举例:
public class NetworkNext {
public static void main(String[] args) {
Server server = new Server();
// 让代理类ProxyServer帮被代理类Server类做事(代理类可以作其被代理类没有的事)
// server.browse();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
// 被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
// 代理类
class ProxyServer implements NetWork {
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check1() {
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check1();
work.browse();
check2();
}
public void check2() {
System.out.println("联网之后的检查工作");
}
}
-------------------------------------------------------------------------
运行结果:
联网之前的检查工作
真实的服务器访问网络
联网之后的检查工作
动态代理需要设计反射的知识,这里先不将。
4、接口的应用:工厂模式
- 工厂模式:实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
- 具体模式
- 简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
- 工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)
- 抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
5、接口和抽象类之间的对比
在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类,要么实现接口。
😜 相 见 就 是 【 猿 分 】 🐒
.
👀 感谢您阅读完此文章 👀
.
❌ 如果文章中有错误的地方请指正 ✔️
.
✏️ 此文章参考尚硅谷教程笔记、Java相关书籍和个人学习经验总结的Java后端学习笔记 ⭐️
.
❓ 希望能够对你有所帮助 🎯
.
🔗 欢迎 【关注】【评论】【点赞】🚩
.
💪 感谢支持,一起加油,共同进步 🏃