JAVA学习笔记_面向对象
1. 面向对象
1.1 面向对象的思想
-
概述
Java语言是一种面向对象的程序设计语言,而面向对象思想是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。 这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。面向对象思想就是在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽象出来,描述成计算机事件的设计思想。 它区别于面向过程思想,强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。
面向过程:强调步骤。
面向对象:强调对象,这里的对象就是洗衣机
-
三大特征:
- 继承
- 封装
- 多态
实例:输出数组
package cn.itcast.day06.demo01;
import java.util.Arrays; // 导入Arrays类
public class Demo01PrintArray {
public static void main(String[] args) {
int[] array = { 10, 20, 30, 40, 50, 60 };
// 要求打印格式为:[10, 20, 30, 40, 50]
// 使用面向过程,每一个步骤细节都要亲力亲为。
System.out.print("[");
for (int i = 0; i < array.length; i++) {
if (i == array.length - 1) { // 如果是最后一个元素
System.out.println(array[i] + "]");
} else { // 如果不是最后一个元素
System.out.print(array[i] + ", ");
}
}
System.out. println("==============");
// 使用面向对象
// 找一个JDK给我们提供好的Arrays类,
// 其中有一个toString方法,直接就能把数组变成想要的格式的字符串
System.out.println(Arrays.toString(array));
}
}
- 特点: 特点面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态 。
1.2 类和对象
-
什么是类:
- 类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
- 现实中,描述一类事物:
- 属性:就是该事物的状态信息。
- 行为:就是该事物能够做什么。
-
什么是对象:
一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性和行为。
-
类与对象的关系 :
- 类是对一类事物的描述,是抽象的。
- 对象是一类事物的实例,是具体的。
- 类是对象的模板,对象是类的实体。
1.3 类的定义
- 属性:事物的状态信息。
- 行为:事物能够做什么。
public class ClassName {
//成员变量
//成员方法
}
- 定义类:就是定义类的成员,包括成员变量和成员方法。
- 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
- 成员方法:把
static
去掉
定义一个Student
类:
public class Student {
// 成员变量
String name; // 姓名
int age; // 姓名
// 成员方法
public void eat() {
System.out.println("吃饭饭!");
}
public void sleep() {
System.out.println("睡觉觉!");
}
public void study() {
System.out.println("学习!");
}
}
成员变量(属性):
String name; // 姓名
int age; // 年龄
成员方法(行为):
public void eat() {} // 吃饭
public void sleep() {} // 睡觉
public void study() {} // 学习注意事项:
- 成员变量是直接定义在类当中的,在方法外边。
- 成员方法不要写
static
关键字。
1.4 类的对象的创建和使用
- 创建的一个Student的对象
public class Student {
public static void main(String[] args) {
// 1. 导包。
import XXX.XXX.Student;
// 2. 创建,格式:
// 类名称 对象名 = new 类名称();
Student stu = new Student(); // 根据Student类,创建了一个名为stu的对象
// 3. 使用其中的成员变量,格式:
// 对象名.成员变量名
System.out.println(stu.name); // null
System.out.println(stu.age); // 0
System.out.println("=============");
// 改变对象当中的成员变量数值内容
// 将右侧的字符串,赋值交给stu对象当中的name成员变量
stu.name = "小明";
stu.age = 18;
System.out.println(stu.name); // 小明
System.out.println(stu.age); // 18
System.out.println("=============");
// 4. 使用对象的成员方法,格式:
// 对象名.成员方法名()
stu.eat();
stu.sleep();
stu.study();
}
}
- 导包:也就是指出需要使用的类,在什么位置。
import 包名称.类名称;
对于和当前类属于同一个包的情况,可以省略导包语句不写。- 创建,格式:
类名称 对象名 = new 类名称();
Student stu = new Student();- 使用,分为两种情况:
使用成员变量:对象名.成员变量名
使用成员方法:对象名.成员方法名(参数)注意事项:
如果成员变量没有进行赋值,那么将会有一个默认值,规则和数组一样。
1.5成员变量和局部变量的区别
在类中的位置不一样
成员变量:类中,方法外
局部变量:方法中或者方法声明上(形式参数)
-
作用的范围不一样
- 成员变量:类中
- 局部变量:方法中
-
**初始化值的不同 **
- 成员变量:有默认值
- 局部变量:没有默认值。必须先定义,赋值,最后使用
-
**在内存中的位置不同 **
- 成员变量:堆内存
- 局部变量:栈内存
-
**生命周期不同 **
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
1.6 内部类
内部类就是一个类里面还包含另一个类
分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
1.6.1 内部类的格式
-
成员内部类
格式:
修饰符 class 外部类名称 { 修饰符 class 内部类名称 { // ... } // ... }
注意:内用外,随意访问;外用内,需要内部类对象。
-
局部内部类
如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。只有当前所属的方法才能使用它,出了这个方法外面就不能用了。
格式:
修饰符 class 外部类名称 { 修饰符 返回值类型 外部类方法名称(参数列表) { class 局部内部类名称 { // ... } } }
-
类的权限修饰符(能否使用)
修饰符 外部类 成员内部类 局部内部类 public Y Y N protected Y Y N default N Y N private N Y N
1.6.2 内部类使用
成员内部类的使用
-
两种方法
-
间接方式:
在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。
-
直接方法:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称(); //或 如果内部类使用Static修饰则可使用这种方式去创建内部类的对象 外部类名称.内部类名称 对象名 = new 外部类名称.内部类名称();
例如:
public class Car { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } public void methodCar() { System.out.println("外部Car的方法"); // 调用内部类的方法 Engine engine = new Engine(); engine.methodEngine(); } // 定义一个内部类 public static class Engine { // 内部类的方法 public void methodEngine() { System.out.println("发动机点火"); } } }
-
局部内部类的使用
局部内部类,只有当前所属的方法才能使用它。
注意:
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效
final
的】。备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。
原因:
- new出来的对象在堆内存当中。
- 局部变量是跟着方法走的,在栈内存当中。
- 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
- 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
1.6.3 * 匿名内部类
匿名内部类是为了省略接口的实现类,直接使用new
来重写接口的抽象方法。
格式:
接口名称 对象名 = new 接口名称() {
// 覆盖重写所有抽象方法
};
public class Demo03AnonymityClass {
public static void main(String[] args) {
MyInterface myInterface = new MyInterface() {
@Override
public void method() {
System.out.println("匿名类重写抽象方法");
}
};
myInterface.method();
}
}
注意事项:
- 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
- 匿名对象,在【调用方法】的时候,只能调用唯一一次。
- 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
2. 面向对象特征——封装
2.1封装的概念
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
2.2 封装的关键字——private
private
的含义:
private是一个权限修饰符,代表最小权限。
可以修饰成 员变量和成员方法。
被private修饰后的成员变量和成员方法,只在本类中才能访问。
-
private
的使用格式private 数据类型 变量名 ;
-
**使用
private
修饰成员变量,代码如下 **public class Student { private String name; private int age; }
注: 使用了
private
本类可以直接访问,但是在类外无法直接进行访问 -
使用
setXxx
方法、getXxx
访问间接访问private
的成员变量public class Student { private String name; private int age; public void setName(String n) { name = n; } public String getName() { return name; } public void setAge(int a) { age = a; } public int getAge() { return age; } }
注:必须叫setXxx或者是getXxx命名规则。
对于Getter来说,不能有参数,返回值类型和成员变量对应;
对于Setter来说,不能有返回值,参数类型和成员变量对应。 -
对于
boolean
类型的private
成员变量的Getter
方法public class Person { private boolean male; //性别 public void setMale(boolean male) { this.male = male; } public boolean isMale() { return male; } }
2.3封装优化——this关键字
this
的使用:
public class Person {
String name; // 我自己的名字
// 参数name是对方的名字
// 成员变量name是自己的名字
public void sayHello(String name) {
System.out.println(name + ",你好。我是" + this.name);
System.out.println(this);
}
}
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量名
通过谁调用的方法,谁就是this
。
2.4 封装优化——构造方法
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象时,其实就是在调用构造方法。
-
格式:
public 类名称(参数类型 参数名称) { 方法体 }
package cn.fate.java_learn.basic.Class; import javax.xml.namespace.QName; public class Student { private String name; private int age; public void setName(String name) { this.name = name; } public String getName() { return name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student() { System.out.println("无参数构造方法"); } public Student(String name, int age) { System.out.println("带参数的构造方法"); this.name = name; this.age = age; } public void show() { System.out.println("我叫:" + name + ",年龄:" + age); } }
注:
- 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。
- 构造方法不要写返回值类型、void都不写。
- 构造方法不能
return
一个具体的返回值。 - 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。
public Student() {}
- 一旦编写了至少一个构造方法,那么编译器将不再赠送。
- 构造方法也是可以进行重载的。 重载:方法名称相同,参数列表不同。
2.5 标准代码——JavaBean
一个标准的类通常要拥有下面四个组成部分:
- 所有的成员变量都要使用private关键字修饰
- 为每一个成员变量编写一对儿Getter/Setter方法
- 编写一个无参数的构造方法
- 编写一个全参数的构造方法
这样标准的类也叫做Java Bean
- 编写一个标准的类
package cn.fate.java_learn.basic.Class;
public class StandardStudent {
private String name;
private int age;
private boolean Male;
public StandardStudent() {
}
public StandardStudent(String name, int age, boolean male) {
this.name = name;
this.age = age;
Male = male;
}
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 boolean isMale() {
return Male;
}
public void setMale(boolean male) {
Male = male;
}
}
注 :
IDEA
可以使用快捷键Alt+Insert
快速生成Getter、Setter 及构造方法
2.6 匿名对象
创建对象的标准格式:
类名称 对象名 = new 类名称();
匿名对象就是只有右边的对象,没有左边的名字和赋值运算符。
new 类名称();
注意事项:匿名对象只能使用唯一的一次,下次再用不得不再创建一个新对象。
使用建议:如果确定有一个对象只需要使用唯一的一次,就可以用匿名对象。
import java.util.Scanner;
public class DemoAnonymous {
public static void main(String[] args) {
methodParam(new Scanner(System.in));
Scanner sc = methodReturn();
int num = sc.nextInt();
System.out.println("Input num is" + num);
methodParam(new Scannner); // 匿名对象充当参数
}
public static void methodParam(Scanner sc){
int num = sc.nextInt();
System.out.printf("Input num is % d\n", num);
}
public static Scanner methodReturn(){
return new Scanner(System.in); // 返回匿名对象
}
}
3. 面向对象特征——继承
3.1 继承的概念
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
3.2 类的继承格式
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
class 父类 {
}
class 子类 extends 父类 {
}
3.3 继承中的成员变量的关系
父子类的继承关系当中,如果成员变量重名,则创建子类对象时,访问有两种方式:
直接通过子类对象访问成员变量:
等号左边是谁,就优先用谁,没有则向上找。间接通过成员方法访问成员变量:
该方法属于谁,就优先用谁,没有则向上找。当出现三种情况时的调用关系:
- 局部变量:
直接写成员变量名
- 本类的成员变量:
this.成员变量名
- 父类的成员变量:
super.成员变量名
注意事项:
无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类的
// 父类
public class Fu {
int num = 10;
}
// 子类
public class Zi extends Fu {
int num = 20;
public void method() {
int num = 30;
System.out.println(num); // 30,局部变量
System.out.println(this.num); // 20,本类的成员变量
System.out.println(super.num); // 10,父类的成员变量
}
}
3.4 重写(Override)
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表也一样。
重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
重载(Overload):方法的名称一样,参数列表【不一样】。方法的覆盖重写特点:创建的是子类对象,则优先用子类方法。
方法覆盖重写的注意事项:
必须保证父子类之间方法的名称相同,参数列表也相同。
@Override
:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。子类方法的返回值必须【小于等于】父类方法的返回值范围。
小扩展提示:java.lang.Object
类是所有类的公共最高父类(祖宗类),java.lang.String
**就是Object
的子类。子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展提示:public > protected > (default) > private
备注:(default)
不是关键字default
,而是什么都不写,留空。
3.5 构造方法的访问特点
继承关系中,父子类构造方法的访问特点:
- 子类构造方法当中有一个默认隐含的“
super()
”调用,所以一定是先调用的父类构造,后执行的子类构造。- 子类构造可以通过
super
关键字来调用父类重载构造。super
的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super
构造总结:
子类必须调用父类构造方法,不写则赠送super()
;写了则用写的指定的super
调用,super
只能有一个,还必须是第一个。
3.6 super关键字的三种用法:
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
3.7 this关键字的三种用法:
- 在本类的成员方法中,访问本类的成员变量。
- 在本类的成员方法中,访问本类的另一个成员方法。
- 在本类的构造方法中,访问本类的另一个构造方法。
在第三种用法当中要注意:
- this(...)调用也必须是构造方法的第一个语句,唯一一个。
super
和this
两种构造调用,不能同时使用。总结:
super
关键字用来访问父类内容,而this
关键字用来访问本类内容
3.8 Java 继承的关系
3.9 抽象类
抽象方法:就是加上
abstract
关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之前写上abstract
即可。如何使用抽象类和抽象方法:
- 不能直接创建
new
抽象类对象。- 必须用一个子类来继承抽象父类。
- 子类必须覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现):子类去掉抽象方法的abstract
关键字,然后补上方法体大括号。- 创建子类对象进行使用。
// 父类
public abstract class Animal {
// 定义一个抽象方法
public abstract void eat();
// 定义一个普通的方法
public void method() {
System.out.printf("普通方法");
}
}
//子类
public class Cat extends Animal {
public void eat(){
System.out.println("Cat eats fish");
}
}
// Main
public class DemoAnimal {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
}
}
注意事项:
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,以满足特殊类的需求。
抽象类的子类(除子类也为抽象类),必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。
- 理解:假设不重写所有抽象方法,则类中可能包含抽象方法没有意义,无法被使用。
练习:红包随机分割。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Random;
public class RandomSplit {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList();
list = divived(100, 20);
System.out.println(list);
}
public static ArrayList<Integer> divived(double money, int n) {
/*
* 红包拆分方法
* @param money 被拆分的总金额 (单位元)
* @param n 被拆分的红包个数
* @return 拆分后的每个红包金额数组
*/
// 创建一个长度的红包数组
ArrayList<Integer> redList = new ArrayList<>();
int moneyFen = (int) (money * 100);
// 判断红包的总金额
if (money > 200) {
System.out.println("单个红包不能超过200元");
return redList; // 返回空的红包集合
}
if (moneyFen < n || moneyFen < 1) {
System.out.println("被拆分的总金额不能小于0.01元");
return redList; // 返回空的红包集合
}
// 创建一个n长的数组
Integer[] array = new Integer[n];
//1. 给每个包分0.01元,确保每个包有钱
Arrays.fill(array, 1);
moneyFen -= n; //总金额减去已分配的0.01 * n 元
//2. 进行随机分配
Random rand = new Random();
while (moneyFen > 1) {
int moneyCut = rand.nextInt(moneyFen); // 随机选择分配的包
int i = rand.nextInt(n); // 随机选择分配的包
array[i] += moneyCut; // 分配给每一个包并且转换为元
moneyFen -= (int) moneyCut;
}
//把最后的包放到最后一个包里面
array[n - 1] += moneyFen ;
// 把分好的包放到redList里面
Collections.addAll(redList, array);
return redList;
}
}
4. 面向对象特征——多态
4.1 多态的概念
多态是同一个行为具有多个不同表现形式或形态的能力。
多态存在的三个必要条件:
- 继承
- 重写
- 父类引用指向子类对象
4.2 多态的个格式
格式:
父类名称 对象名 = new 子类名称(); // 或者: 接口名称 对象名 = new 实现类名称();
4.3 多态中的成员变量和成员方法
访问成员变量的两种方式:
- 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
- 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
成员方法的访问规则是:
看new
的是谁,就优先用谁,没有则向上找。成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
口诀: 编译看左边,运行看右边。
4.4 对象的转型
-
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。当父类引用指向一个子类对象时,便是向上转型 。
向上转型是安全的,等于从小范围转向大的范围。
格式:
父类类型 变量名 = new 子类类型(); // 比如 Animal a = new Cat();
-
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
注: 向下转型的对象,之前的必须是通过向上转型过来的,才能向下转回原来的对象。
格式:
子类类型 变量名 = (子类类型) 父类变量名; // 比如: Cat c =(Cat) a;
向下转型的异常:
如果错误的转换为其他对象,编译时会出现
ClassCastException
的报错。为了确定向下转型的正确性,我们可以使用关键字instanceof
,进行校验。校验格式:
变量名 instanceof 数据类型 如果变量属于该数据类型,返回true。 如果变量不属于该数据类型,返回false。
实例:
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 if (a instanceof Cat) { Cat c = (Cat) a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } else if (a instanceof Dog) { Dog d = (Dog) a; d.watchHouse(); // 调用的是 Dog 的 watchHouse } } }
5. Java中的接口
接口就是多个类的公共规范。
接口(Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
如果是Java 7,那么接口中可以包含的内容有:
- 常量
- 抽象方法
如果是Java 8,还可以额外包含有:
- 默认方法
- 静态方法
如果是Java 9,还可以额外包含有:
- 私有方法
4.1 定义接口的格式
public interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
接口有以下特性:
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
4.2 接口的使用步骤
- 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
格式:
public class 实现类名称 implements 接口名称 {
// ...
}
接口的实现类必须覆盖重写(override)接口中所有的抽象方法。
创建实现类的对象,进行使用。
4.3 接口中抽象(abstract)方法
在任何版本的Java中,接口都能定义抽象方法。
格式:public abstract 返回值类型 方法名称(参数列表){ //方法体 };
注意事项:
- 接口当中的抽象方法,修饰符必须是两个固定的关键字:
public
abstract
public
abstract
关键字修饰符可以选择性地省略。- 方法的三要素,可以随意定义。
- 实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。
public interface MyInterfaceAbstract {
// 这是一个抽象方法
public abstract void methodAbs1();
// 这也是抽象方法
abstract void methodAbs2();
// 这也是抽象方法
public void methodAbs3();
// 这也是抽象方法
void methodAbs4();
}
4.4 接口中的默认(default)方法
从Java 7开始,接口当中允许定义Default方法。
接口中使用的默认方法,可以解决接口升级的问题。默认方法,实现类可以不重写。
格式:
public default 返回值类型 方法名称(参数列表) { 方法体 }
注:
- 关键字
public
修饰符可以选择性地省略。- 接口的默认方法,可以通过接口实现类对象,直接调用。
- 接口的默认方法,也可以被接口实现类进行覆盖重写。
public interface MyInterfaceDefault {
// 抽象方法
public abstract void methodAbstract();
// 默认方法
public default void methodDefault() {
System.out.println("默认方法");
}
}
4.5 接口中的私有(private)方法
从Java 9开始,接口当中允许定义私有方法。
- 普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表) { 方法体 }
- 静态私有方法,解决多个静态方法之间重复代码问题
格式:private static 返回值类型 方法名称(参数列表) { 方法体 }
public interface MyInterfacePrivate {
public default void methodDefault1() {
System.out.println("默认方法1");
methodDefaultCommon();
}
public default void methodDefault2() {
System.out.println("默认方法2");
methodDefaultCommon();
}
public static void methodStatic1() {
System.out.println("静态方法1");
methodStaticCommon();
}
public static void methodStatic2() {
System.out.println("静态方法2");
methodStaticCommon();
}
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
private void methodDefaultCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
4.6 接口中的静态(static)方法
从Java 8开始,接口当中允许定义静态方法。
格式:public static 返回值类型 方法名称(参数列表) { 方法体 }
注 :
- 关键字
public
修饰符可以选择性地省略。- 静态方法的调用通过接口名称直接调用,而不是使用实现类的对象进行调用,因为静态和对象没关系。
public interface MyInterfaceStatic {
public static void methodStatic() {
System.out.println("这是接口的静态方法!");
}
}
4.7 接口中的常量的定义和使用
接口当中也可以定义“成员变量”,但是必须使用
public
static
final
三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:public static final 数据类型 常量名称 = 数据值; // public static final 都可以省略 数据类型 常量名称 = 数据值;
备注:
一旦使用final
关键字进行修饰,说明不可改变。备注:
一旦使用final
关键字进行修饰,说明不可改变。注意事项:
- 接口当中的常量,可以省略
public
static
final
,注意:其实都可以省略。- 接口当中的常量,必须进行赋值;不能不赋值。
- 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)
- 调用常量时使用接口名称去调用。
4.8 接口中方法的总结
- 成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;
注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。
- 接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);
注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。
- 从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表) { 方法体 }
注意:默认方法也可以被覆盖重写
注意:默认方法也可以被覆盖重写
- 从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表) { 方法体 }
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
- 从Java 9开始,接口里允许定义私有很乏,格式:
普通私有方法:private 返回值类型 方法名称(参数列表) { 方法体 }
静态私有方法:
private static 返回值类型 方法名称(参数列表) { 方法体 }
注意:private的方法只有接口自己才能调用,不能被实现类或别人使用。
4.9 类的继承和实现接口的问题
类的继承关系是单继承的,但类可同时实现多个接口。
使用接口时的注意事项:
- 接口是没有者构造方法的。
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
- 实现类所实现的多个接口当中,存在重复的抽象方法,只需要覆盖重写一次即可。
- 实现类没有覆盖重写所有接口当中的所有抽象方法,实现类就必须是一个抽象类。
- 实现类实现的多个接口当中,存在重复的默认方法,实现类一定要对冲突的默认方法进行覆盖重写。
- 一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先用父类当中的方法。
4.10 接口之间的多继承
在Java中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。
格式:
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB { // 覆盖重写所有抽象方法 }
注意事项:
- 多个父接口当中的抽象方法如果重复,由于实现类中必重写抽象方法,说以没影响。
- 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写。
综合案例
-
问题:
笔记本电脑(laptop)通常具备使用USB设备的功能。在生产时,笔记本都预留了可以插入USB设备的USB接口,但具体是什么USB设备,笔记本厂商并不关心,只要符合USB规格的设备都可以。定义USB接口,具备最基本的开启功能和关闭功能。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,实现USB接口,否则鼠标和键盘的生产出来也无法使用。
-
分析:
- USB接口,包含开启功能、关闭功能
- 笔记本类,包含运行功能、关机功能、使用USB设备功能
- 鼠标类,要实现USB接口,并具备点击的方法
- 键盘类,要实现USB接口,具备敲击的方法
// Usb接口 public interface Usb { public abstract void on(); public abstract void off(); } // Mouse 鼠标类 public class Mouse implements Usb { @Override public void on() { System.out.println("开启鼠标"); } @Override public void off() { System.out.println("关闭鼠标"); } public void click() { System.out.println("鼠标点击"); } } // 键盘类 public class Keboard implements Usb { @Override public void on() { System.out.println("开启键盘"); } @Override public void off() { System.out.println("关闭键盘"); } public void inputs() { System.out.println("键盘输出"); } } // 电脑类 public class Computer { public void powerOn() { System.out.println("开启电脑"); } public void powerOff() { System.out.println("关闭电脑"); } public void useDevice(Usb usb) { // 开启Usb设备 usb.on(); // 使用Usb设备 if (usb instanceof Keboard) { // 向下转换为键盘 Keboard keboard = (Keboard) usb; keboard.inputs(); } else if (usb instanceof Mouse) { // 向下转换为鼠标 Mouse mouse = (Mouse) usb; mouse.click(); } // 关闭usb设备 usb.off(); } } // 测试 public class DemoMain { public static void main(String[] args) { Computer computer = new Computer(); // 开启电脑 computer.powerOn(); // 向上转换出一个use Usb usb = new Mouse(); computer.useDevice(usb); // 直接传入一个usb设备 Keboard keboard = new Keboard(); computer.useDevice(keboard); // 掉用过程: 参数传过去先向上转换, 再向下转换。 // 关闭电脑 computer.powerOff(); } }