专题三 面向对象
/*
- 内存结构:
栈(局部变量)、
堆(new出来的结构:对象(非static成员变量)、数组)
- 一、Java面向对象学习的三条主线:
- 1.Java类及类的成员:属性、方法、构造器;代码块、内部类
- 2.面向对象的三大特征:封装性、继承性、多态性、(抽象性)
能不能获取 继承性
能不能调 封装性
判断能不能继承(该继承)A is B
*
- 3.其它关键字:this、super、static、final、abstract、interface、package、import等
- “大处着眼,小处着手” 抽取
- 面向过程(POP) 与 面向对象(OOP)
-
二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的 是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对 象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
-
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如 抽象、分类、继承、聚合、多态等。
- 二、“人把大象装进冰箱”
- 1.面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
- ① 把冰箱门打开
- ② 抬起大象,塞进冰箱
- ② 把冰箱门关闭
- 2.面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
- 人{
-
打开(冰箱){
-
冰箱.开开();
-
}
-
抬起(大象){
-
大象.进入(冰箱);
-
}
-
关闭(冰箱){
-
冰箱.闭合();
-
}
- }
- 冰箱{
-
开开(){}
-
闭合(){}
- }
- 大象{
-
进入(冰箱){
-
}
- }
- 三、面向对象的两个要素:
- 类:对一类事物的描述,是抽象的、概念上的定义
- 对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
-
面向对象程序设计的重点是类的设计
-
设计类,就是设计类的成员。
/
/
-
一、设计类,其实就是设计类的成员
-
属性 = 成员变量 = field = 域、字段
-
方法 = 成员方法 = 函数 = method
-
创建类的对象 = 类的实例化 = 实例化类
-
二、类和对象的使用(面向对象思想落地的实现):
-
1.创建类,设计类的成员
-
2.创建类的对象
-
3.通过“对象.属性”或“对象.方法”调用对象的结构 参数
后续:有了对象反映推出类。反射
*
- 三、如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
- 意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
- 四、对象的内存解析
{{uploading-image-509687.png(uploading...)}}
属性存在堆空间中
首地址值
/
/
- 类中属性的使用
变量:成员变量 vs 局部变量(方法内、方法形参、构造器内、构造器形参、代码块内)
- 属性(成员变量) vs 局部变量
- 1.相同点:
-
1.1 定义变量的格式:数据类型 变量名 = 变量值
-
1.2 先声明,后使用
-
1.3 变量都有其对应的作用域
- 2.不同点:
-
2.1 在类中声明的位置的不同
-
属性:直接定义在类的一对{}内
-
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
形参是parameter,实参是argument *
2.2 关于权限修饰符的不同
-
属性:可以在声明属性时,指明其权限,使用权限修饰符。
-
常用的权限修饰符:private、public、缺省、protected --->封装性
-
目前,大家声明属性时,都使用缺省就可以了。
-
局部变量:不可以使用权限修饰符。可以理解方法规定了权限,和方法一致
-
2.3 默认初始化值的情况:
-
属性:类的属性,根据其类型,都有默认初始化值。
-
整型(byte、short、int、long):0
-
浮点型(float、double):0.0
-
字符型(char):0 (或'\u0000')
-
布尔型(boolean):false
-
引用数据类型(类、数组、接口):null
-
局部变量:没有默认初始化值。local 本地的局部的变量
-
意味着,我们在调用局部变量之前,一定要显式赋值。
-
特别地:形参在调用时,我们赋值即可。
-
2.4 在内存中加载的位置:
-
属性:加载到堆空间中 (非static)
-
局部变量:加载到栈空间
/
/
- 类中方法的声明和使用
- 方法:描述类应该具有的功能。
- 比如:Math类:sqrt()\random() ...
-
Scanner类:nextXxx() ...
-
Arrays类:sort() \ binarySearch() \ toString() \ equals() \ ...
- 1.举例:
- public void eat(){}
- public void sleep(int hour){}
- public String getName(){}
- public String getNation(String nation){}
-
- 方法的声明:权限修饰符 返回值类型 方法名(形参列表){
-
方法体
-
}
- 注意:static、final、abstract 来修饰的方法,后面再讲。
-
- 说明:
-
3.1 关于权限修饰符:默认方法的权限修饰符先都使用public
-
Java规定的4种权限修饰符:private、public、缺省、protected -->封装性再细说
-
3.2 返回值类型: 有返回值 vs 没有返回值
-
3.2.1 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
-
return关键字来返回指定类型的变量或常量:“return 数据”。
-
如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要
-
使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思。(return ; 以后的语句不执行)
-
3.2.2 我们定义方法该不该有返回值?
-
① 题目要求
-
② 凭经验:具体问题具体分析
-
3.3 方法名:属于标识符,遵循标识符的规则和规范,“见名知意”
-
3.4 形参列表: 方法可以声明0个,1个,或多个形参。
-
3.4.1 格式:数据类型1 形参1,数据类型2 形参2,...
-
3.4.2 我们定义方法时,该不该定义形参?
-
① 题目要求
-
② 凭经验:具体问题具体分析
-
3.5 方法体:方法功能的体现。
- 4.return关键字的使用:
-
1.使用范围:使用在方法体中
-
2.作用:① 结束方法
-
② 针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。
-
3.注意点:return关键字后面不可以声明执行语句。
-
- 方法的使用中,可以调用当前类的属性或方法
-
特殊的:方法A中又调用了方法A:递归方法。
-
方法中,不可以定义方法。
*/
练习:
有返回值,拿一个变量去接收 返回值
无返回值, 直接输出
思考 变量作为属性好还是形参
main方法调用类中方法,通过造对象 再调用。不用造对象的方法有:
类中声明类中信息的方法 return信息的方法 供外界来调用。
/*
-
- 对象数组题目:
定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
- 对象数组题目:
提示:
- 生成随机数:Math.random(),返回值类型double;
- 四舍五入取整:Math.round(double d),返回值类型long。
- 此代码是对StudentTest.java的改进:将操作数组的功能封装到方法中。
*/
public class StudentTest1 {
public static void main(String[] args) {
//声明Student类型的数组
Student1[] stus = new Student1[20];
for(int i = 0;i < stus.length;i++){
//给数组元素赋值
stus[i] = new Student1();
//给Student对象的属性赋值
stus[i].number = (i + 1);
//年级:[1,6]
stus[i].state = (int)(Math.random() * (6 - 1 + 1) + 1);
//成绩:[0,100]
stus[i].score = (int)(Math.random() * (100 - 0 + 1));
}
StudentTest1 test = new StudentTest1();
//遍历学生数组
test.print(stus);
System.out.println("********************");
//问题一:打印出3年级(state值为3)的学生信息。
test.searchState(stus, 3);
System.out.println("********************");
//问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
test.sort(stus);
//遍历学生数组
test.print(stus);
}
/**
*
* @Description 遍历Student1[]数组的操作
* @author shkstart
* @date 2019年1月15日下午5:10:19
* @param stus
*/
public void print(Student1[] stus){
for(int i = 0;i <stus.length;i++){
System.out.println(stus[i].info());
}
}
/**
*
* @Description 查找Stduent数组中指定年级的学生信息
* @author shkstart
* @date 2019年1月15日下午5:08:08
* @param stus 要查找的数组
* @param state 要找的年级
*/
public void searchState(Student1[] stus,int state){
for(int i = 0;i <stus.length;i++){
if(stus[i].state == state){
System.out.println(stus[i].info());
}
}
}
*
* @Description 给Student1数组排序
* @author shkstart
* @date 2019年1月15日下午5:09:46
* @param stus
*/
public void sort(Student1[] stus){
for(int i = 0;i < stus.length - 1;i++){
for(int j = 0;j < stus.length - 1 - i;j++){
if(stus[j].score > stus[j + 1].score){
//如果需要换序,交换的是数组的元素:Student对象!!!
Student1 temp = stus[j];
stus[j] = stus[j + 1];
stus[j + 1] = temp;
}
}
}
}
}
class Student1{
int number;//学号
int state;//年级
int score;//成绩
//显示学生信息的方法
public String info(){
return "学号:" + number + ",年级:" + state + ",成绩:" + score;
}
}
变量的分类:
第一种:
第二种:
/*
- 一、理解“万事万物皆对象”
- 1.在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
-
>Scanner,String等
-
>文件:File
-
>网络资源:URL
- 2.涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象。
- 二、内存解析的说明
- 1.引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
三、匿名对象的使用
-
1.理解:我们创建的对象,没有显式的赋给一个变量名。即为匿名对象
-
2.特征:匿名对象只能调用一次。
-
3.使用:如下
Phone p = new Phone();
// p = null;
//匿名对象
// mall.show(p); 参数为phone类型的
//匿名对象的使用
mall.show(new Phone());
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
注意以下使用:
此时相当于 把new phone 的地址值 赋给 类 PhoneMall show 方法的形参,形参是局部变量。就可以调用多次 show里的调用是同一对象。
*/
自定义的工具类
1.结构清晰
2.复用性
/*
-
方法的重载(overload) loading... 编程带来便利
-
1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
-
"两同一不同":同一个类、相同方法名
-
参数列表不同:参数个数不同(包括参数顺序),参数类型不同
-
先排查同,在排查不同
-
- 举例:
-
Arrays类中重载的sort() / binarySearch()
-
3.判断是否是重载:
-
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
-
- 在通过对象调用方法时,如何确定某一个指定的方法:
-
方法名 ---> 参数列表 如何调用确定的方法:方法名 -> 参数列表
/
/
- 可变个数形参的方法
- 1.jdk 5.0新增的内容
- 2.具体使用:
- 2.1 可变个数形参的格式:数据类型 ... 变量名
- 2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,。。。
- 2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
- 2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
- 2.5 可变个数形参在方法的形参中,必须声明在末尾
- 2.6 可变个数形参在方法的形参中,最多只能声明一个可变形参。
*/
sql 占位符。也是可变形式参数
/*
*
- 关于变量的赋值:
- 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
- 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
-
= 赋值的前提: 1 类型相同 2 满足自动类型提升
*/
/*
- 方法的形参的传递机制:值传递
- 1.形参:方法定义时,声明的小括号内的参数
- 实参:方法调用时,实际传递给形参的数据
- 2.值传递机制:
- 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
- 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。
*/
不同方法的参数在栈中要标明。
方法执行完就销毁 弹出栈
造对象:何时造对象
static
static : 无需创建实例可直接使用, 会直到程序关闭才会释放资源
new 对象: 每次使用时new一次对象,对象使用完会自动释放资源, 下一次再使用时需要从新new一次
结论:
在频繁需要使用的对象, 建议使用static
注意: 内存是有限的, 好比一个容器, static每用一次就在往容器加一点, 如果滥用static, 可能会造成内存泄漏
堆中的
例子:
//错误的:
// swap(arr[j],arr[j + 1]);
//正确的:
swap(arr,j,j + 1);
//错误的:交换数组中指定两个位置元素的值
// public void swap(int i,int j){
// int temp = i;
// i = j;
// j = temp;
// }
//正确的:交换数组中指定两个位置元素的值
public void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
值传递 练习
首先找main方法
栈空间中声明变量
new对象过程中将类加载进方法区(类中方法已经在方法区)
堆中创建对象,类中存在属性
栈中定义变量分方法内 局部变量 存在同名不同位置 (栈中定义的参数有作用范围 用不同方法划分)
= 赋值意义
执行完 结构销毁 方法内的变量出栈消失 销毁
例题:
答案:
/*
-
递归方法的使用(了解)
-
1.递归方法:一个方法体内调用它自身。
-
- 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
-
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
return 最小case
f(n) 为最大项
递归:画递归树 前序遍历
面向对象的特征一:封装与隐藏 3W:what? why? how?
高内聚,低耦合”。
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
-
一、问题的引入:
-
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。 这里,赋值操作要受到属性的数据类型和存储范围的制约。 除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs())。 // 通过方法做筛选逻辑。不满足可以抛出一个异常。避免忽略约束
同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private). 以此同时,对外提供接口
-->此时,针对于属性就体现了封装性。 -
二、封装性的体现:
-
我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
-
拓展:封装性的体现:① 如上 ② 不对外暴露的私有的方法(内部使用,类内可使用) ③ 单例模式 (构造器私有化了) ...
-
三、封装性的体现,需要权限修饰符来配合。
-
1.Java规定的4种权限(从小到大排列):private、缺省、protected 、public
-
2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
-
3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
##修饰 类 的话,只能使用:缺省、public (内部类可以)
最小的权限 private 可以在类内部使用
- 总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。
*/
其中:The field Order.orderPrivate is not visible
The method methodPrivate() from the type Order is not visible
说明变量和方法被私有化了。不可见 存在
缺省权限 只能在同一包里使用
/*
- 类的结构之三:构造器(或构造方法、constructor)的使用
- construct:建设、建造。 construction:CCB constructor:建设者
- 一、构造器的作用:
- 1.创建对象
- 2.初始化对象的信息
构造器的特征
1.它具有与类相同的名称
2.它不声明返回值类型。(与声明为void不同) 不能被static、final、synchronized、abstract、native修饰
3.不能有return语句返回值
如同我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的 构造器中加入完成“洗澡”的程序代码,于是每个“人”一出生就会自 动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们 要“洗澡”了。
例子:
//构造器
public Person(){
System.out.println("Person().....");
}
public Person(String n){
name = n;
- 二、说明:
- 1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 2.定义构造器的格式:权限修饰符 类名(形参列表){}
- 3.一个类中定义的多个构造器,彼此构成重载
- 4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- 5.一个类中,至少会有一个构造器。
*/
//创建类的对象:new + 构造器
Person p = new Person();
构造器 构造方法不是方法 构造器是造对象的 方法是功能的封装
默认构造器的权限和类的权限一致
一般用空参(反射一般用空参构造器)
属性
构造器 创建对象时初始化 通过写构造器
方法
/*
- 总结:属性赋值的先后顺序
- ① 默认初始化
- ② 显式初始化
- ③ 构造器中初始化
以上操作只执行1次 ,初始化 对象还没创建之前的操作 - ④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
- 以上操作的先后顺序:① - ② - ③ - ④
*/
JavaBean是一种Java语言写成的可重用组件。
所谓javaBean,是指符合如下标准的Java类:
1类是公共的
2有一个无参的公共的构造器
3有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以 用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP 页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用 户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关 心任何改变。
/*
在Java中,this关键字比较难理解,它的作用和其词义很接近。
1.它在方法内部使用,即这个方法所属对象的引用;
2.它在构造器内部使用,表示该构造器正在初始化的对象。
3. this 可以调用类的属性、方法和构造器
4. 什么时候使用this关键字呢?
当在方法内需要用到调用该方法的对象时,就用this。
具体的:我们可以用this来区分属性和局部变量。 比如:this.name = name;
命名要见名知意所以;
{{uploading-image-73324.png(uploading...)}}
变量名赋值就近原则
解决属性和形参重名
此时区分属性 和 形参就用this
-
this关键字的使用:
-
1.this可以用来修饰、调用:属性、方法、构造器
-
2.this修饰属性和方法:
-
this理解为:当前对象 或 当前正在创建的对象(构造器中)
-
2.1 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性或方法。但是,
-
通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式
-
的使用"this.变量"的方式,表明此变量是属性,而非形参。
-
2.2 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用当前正在创建的对象属性或方法。
-
但是,通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式
-
的使用"this.变量"的方式,表明此变量是属性,而非形参。
-
-
this调用构造器
初始化操作冗余 考虑代码精简
1.封装为方法 2.在构造器中调用另一个构造器 对象自始自终就一个this(); 调用空参构造器
-
-
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
-
② 构造器中不能通过"this(形参列表)"方式调用自己
-
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"
-
④ 规定:"this(形参列表)"必须声明在当前构造器的首行
-
⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器。 (多个 ,就满足不了 规定:"this(形参列表)"必须声明在当前构造器的首行)
*/
例子:
public class Girl {
private String name;
private int age;
public Girl() {
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void marry(Boy boy){
System.out.println("我想嫁给" + boy.getName());
boy.marry(this); 这边用this表示当前女孩
}
/*
- 一、package关键字的使用
- 1.为了更好的实现项目中类的管理,提供包的概念
- 2.使用package声明类或接口所属的包,声明在源文件的首行
- 3.包,属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、“见名知意”
- 4.每"."一次,就代表一层文件目录。
- 补充:同一个包下,不能命名同名的接口、类。
-
不同的包下,可以命名同名的接口、类。
-
二、import关键字的使用
-
import:导入
-
- 在源文件中显式的使用import结构导入指定包下的类、接口
-
- 声明在包的声明和类的声明之间
-
- 如果需要导入多个结构,则并列写出即可
-
- 可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
-
- 如果使用的类或接口是java.lang包下定义的,则可以省略import结构. //不用导 默认可以用
-
- 如果使用的类或接口是本包下定义的,则可以省略import结构 隐式导入
-
- 如果在源文件中,使用了不同包下的 同名 的类,则必须至少有一个类需要以全类名的方式显示。
-
- 使用"xxx."方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入。 这个位置的 表示的接口 类不是子包
-
- import static:导入指定类或接口中的静态结构:属性或方法。 落脚点是一个结构不是类 而import落脚点是接口或者接口
9的 例子:System.out.println("hello!"); 替换为
import static java.lang.System.*;
out.println("hello");
*/
为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。可以理解为:“子类 is a 父类” - import static:导入指定类或接口中的静态结构:属性或方法。 落脚点是一个结构不是类 而import落脚点是接口或者接口
/*
- 面向对象的特征之二:继承性 why?
- 一、继承性的好处:
- ① 减少了代码的冗余,提高了代码的复用性
- ② 便于功能的扩展
- ③ 为之后多态性的使用,提供了前提
注意:不要仅为了获取其他类中某个功能而去继承
-
二、继承性的格式:
-
class A extends B{}
-
A:子类、派生类、subclass
-
B:父类、超类、基类、superclass
-
2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。而父类的构造器属于子类可以调用。
-
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
-
只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
父类中声明为private的属性或方法,继承到了, 只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
-
2.2 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
-
子类和父类的关系,不同于子集和集合的关系。子类功能要强大一些
-
extends:延展、扩展
-
三、Java中关于继承性的规定:
-
1.一个类可以被多个子类继承。
-
2.Java中类的单继承性:一个类只能有一个父类。 (类的单继承 接口的多继承) 接口的多实现
-
3.子父类是相对的概念。
-
4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
-
5.子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法 多重继承的优势
-
四、
问题引人:
- 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
-
- 所有的java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类
-
- 意味着,所有的java类具有java.lang.Object类声明的功能。
/
/
- 意味着,所有的java类具有java.lang.Object类声明的功能。
-
方法的重写(override / overwrite)
-
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
-
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
-
- 重写的规定:
-
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
-
//方法体
-
}
-
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
-
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
-
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
-
>特殊情况:子类不能重写父类中声明为private权限的方法
-
③ 返回值类型:
-
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
-
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
-
>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
-
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
-
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
静态的 * static定义的的不能被覆盖,被重写,是随着类的加载而加载 没有小三角 不算重写了
- 面试题:区分方法的重载与重写
1.什么?
2.细节点
3.多态性上的不同:
*/
体会4种不同的权限修饰
//同一个包中的其他类,不可以调用类中私有的属性、方法
//在不同包的子类中,不能调用类中声明为private和缺省权限的属性、方法
//不同包下的普通类(非子类),不可以调用声明为private、缺省、protected权限的属性、方法
super关键字的使用:
场景:方法重写以后:有办法区分重写前的和重写后的方法 即父类被重写的方法和子类重写的方法(子父类相同的方法,区分)
类似:属性和形式参数重名了 用this区分说明我们调用的是属性
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
/*
-
super关键字的使用
-
1.super理解为:父类的
-
2.super可以用来调用:属性、方法、构造器
-
3.super的使用:调用属性和方法
当子父类有同名属性时
子父类的属性不存在重写,默认调用自己的属性
如果调用父类的属性 需要加super关键字
否则调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super.
省略时先在子类寻找,没有时,再去父类中查找 -
3.1 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用
-
父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
-
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的
-
使用"super.属性"的方式,表明调用的是父类中声明的属性。
-
3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的
-
使用"super.方法"的方式,表明调用的是父类中被重写的方法。
-
4.super调用构造器
应用:
当父类属性为private时
通过调用构造器用父类private的属性
-
4.1 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
-
4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
-
4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
-
4.4 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()
-
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
*/构造器 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",子类 默认调用空参构造器,如果此时父类没有空参构造器,就会报错。
注意;
子类中所有的构造器默认都会访问父类中空参数的构造器
当父类中没有空参数的构造器时,子类的构造器必须通过this(参 数列表)或者super(参数列表)语句指定调用本类或者父类中相应的 构造器。同时,只能”二选一”,且必须放在构造器的首行
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又 没有无参的构造器,则编译出错
处理方法:1.父类增加空参构造。
2.调用父类特定的构造器
为什么super(...) 或 this(...) 调用语句只能作为构造器中的第一句出现?
无论通过哪个构造器创建子类对象,需要保证先初始化父类
目的:当子类继承父类后,“继承”父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化。
/*
-
子类对象实例化的全过程
-
- 从结果上来看:(继承性)
-
子类继承父类以后,就获取了父类中声明的属性或方法。
-
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
-
- 从过程上来看:
-
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,...
-
直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有
-
父类中的结构,子类对象才可以考虑进行调用。
没有多态性则 抽象类和接口的存在就没有意义。因为抽象类和接口均不能造对象,现实中,我们会创建对象子类对象或抽象类的实现类。类是继承,而我们希望通过多态性实现接口的多继承。
- 明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
*/
注意:由于某些现实需要 某些属性只能被获取 不能被设置
方式1:设置get方法 不设置set方法
2:修改属性权限
/*
- 面向对象特征之三:多态性
抽象类、接口类的使用体现了多态性
- 1.理解多态性:可以理解为一个事物的多种形态。
- 2.何为多态性:
- 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
-
- 多态的使用:虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
- 总结:编译,看左边;运行,看右边。
Java引用变量有两个类型:编译时类型和运行时类型。
编译时类型由声明 该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
*
- 4.多态性的使用前提: ① 类的继承关系 ② 方法的重写
属性不存在覆盖 属性(编译和运行都看左边)
- 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
注意:
1.
编译时 看左边 父类 此时如果点,查看源码的时候,进入的是父类的方法
运行时 看右边 子类 运行的是子类的方法
*/
为什么要用多态性?
创建所在类的对象,才能调用类属性方法
如果不用多态性,需要写好多重载的方法
解释:原方法定义方法参数用父类对象,这样以后通过多态性(即父类的引用指向子类的对象)根据传入的不同的子类实例参数编译时检查父类的方法(其中的方法已经被子类重写了),运行时根据传入实例参数类型的不同,实现不同子类的方法。减少了很多重载方法的代码。
原方法定义时用父类为方法参数,这样以后可以用子类的对象去赋值,通用性强
例如:多态性的应用。多态性的体现
1.equals( object obj) 用为子类的对象去赋值
- 数据库的连接 根据不同的数据库 选择运行数据库方法
多态性的使用:虚拟方法调用
多态性对类的属性没用。编译和运行都看左边
- 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
- 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
- 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
- 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边
注意:
声明时是父类 而new的时候是子类对象
调用方法时体现的是虚拟方法调用
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,是可见的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法?
类类型 使用强制类型转换符
基本类型 自动类型提升
方法;向下转型:使用强制类型转换符。
使用强转时,可能出现ClassCastException的异常。
如何避免:
/*
* instanceof关键字的使用 判断 变量A 的本质类
*
* a instanceof A:判断对象(变量)a是否是类A的实例。如果是,返回true;如果不是,返回false。
*
*
* 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先
* 进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
*
* 如果 a instanceof A返回true,则 a instanceof B也返回true.
* 其中,类B是类A的父类。
*/
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同 赋值操作 就是地址值的赋值
/*
- java.lang.Object类
- 1.Object类是所有Java类的根父类
- 2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
- 3.Object类中的功能(属性、方法)就具有通用性。
- 属性:无
- 方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
-
wait() 、 notify()、notifyAll()
-
- Object类只声明了一个空参的构造器
Order order = new Order();
System.out.println(order.getClass().getSuperclass()); 获取该对象是那个类,以及该类的父类
获取order类的父类名称
finalize( )
回收垃圾之前,垃圾回收调用当前对象的finalize方法 回收对象
p = null;//此时对象实体就是垃圾对象,等待被回收。但时间不确定。
System.gc();//强制性释放空间。 通知垃圾回收
通过子类重写此方法 finalize( ),可在释放对象前进行某些操作
@Override
protected void finalize() throws Throwable {
System.*out*.println("对象被释放--->" + this);
}
- 面试题:
- final、finally、finalize的区别?
*/
如何重写equals()方法
== 符号使用时,必须保证符号左右两边的变量类型一致。 否则编译都过不去,报错
== 和 equals()的坑
以下equals为未重写过。此时 判断逻辑 是 ==
Order order1 = new Order(1001, "BB");
Order order2 = new Order(1001, new String("BB"));
Order order3 = new Order(1001, "BB");
System.out.println(order1.equals(order3)); true
System.out.println(order1.equals(order2)); false
原因:由于String 存在常量池 如果不是new的话 指向的是同一地址
引用类型尽量用equals()
/*
-
Object类中toString()的使用:
-
- 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
-
- Object类中toString()的定义:
-
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
} -
- 像String、Date、File、包装类等都重写了Object类中的toString()方法。
-
使得在调用对象的toString()时,返回"实体内容"信息
-
- 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"
例如:
public class Customer {
public String toString() {
return " Customer [name=" + name + ", age=" + age + "]";
}
} // 包含输出类类型 写法
*/
/*
- 包装类的使用:
我们希望java的基本数据类型也让它具有特征和功能,给基本数据类型对应包装类 - 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
-
2.掌握的:基本数据类型、包装类、String三者之间的相互转换
-
注意:
基本数据类型 --->包装类:调用包装类的构造器 传入需要对象时
包装类--->基本数据类型:调用包装类Xxx的xxxValue() 类的对象不能做加减乘除运算 因此需要转换/*
-
JDK 5.0 新特性:自动装箱 与自动拆箱
*/基本数据类型包装成包装类的实例 ---装箱
获得包装类对象中包装的基本类型变量
JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
特别是:
class Order{
boolean isMale;
Boolean isFemale;
}
Order order = new Order();
System.out.println(order.isMale);//false 默认值
System.out.println(order.isFemale);//null 默认值 -
*/
String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
例如:
int num1 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式2:调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);//"12.3"
基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
例如:
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);
String str2 = "true1";
boolean b1 = Boolean.parseBoolean(str2);
System.out.println(b1);
/*
- Java中的JUnit单元测试
- 步骤:
- 1.选中当前工程 - 右键选择:build path - add libraries - JUnit 4 - 下一步
- 2.创建Java类,进行单元测试。
- 此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器
- 3.此类中声明单元测试方法。
- 此时的单元测试方法:方法的权限是public,没有返回值,没有形参
- 4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
- 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
- 6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
- 说明:
- 1.如果执行结果没有任何异常:绿条
- 2.如果执行结果出现异常:红条
*/
关键字:static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,
这时系统才会分配内存空间给对象, 其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,
某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,
不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。
/*
- static关键字的使用
- 1.static:静态的
- 2.static可以用来修饰:属性、方法、代码块、内部类
- 3.使用static修饰属性:静态变量(或类变量)
3.1 属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
-
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的
-
非静态属性时,不会导致其他对象中同样的属性值的修改。
-
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致
-
其他对象调用此静态变量时,是修改过了的。
3.2 static修饰属性的其他说明:
- ① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
-
② 静态变量的加载要早于对象的创建。(实例变量 随着对象的创建而加载)
-
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
-
④ 类变量 实例变量
-
类 yes no
-
对象 yes yes
-
3.3 静态属性举例:System.out; Math.PI;
内存模型:
-
4.使用static修饰方法:静态方法
-
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
-
② 静态方法 非静态方法
-
类 yes no
-
对象 yes yes
-
③ 静态方法中,只能调用静态的方法或属性 理解(类随着类的加载而加载 非静态的方法或属性随着对象的创建而加载 可以结合生命周期理解 )
-
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
静态属性 只有一份
-
- static注意点:
- 5.1 在静态的方法内,不能使用this关键字、super关键字
- 5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。(类的生命周期 对象的生命周期)
static的使用时机
-
- 开发中,如何确定一个属性是否要声明为static的?
-
> 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
-
> 类中的常量也常常声明为static
- 开发中,如何确定一个方法是否要声明为static的?
-
> 操作静态属性的方法,通常设置为static的
-
> 工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections (这样就没有必要造对象 再调用)
*/
static的应用:
private static int init = 1001;//static声明的属性被所有对象所共享
public Circle(){
id = init++;
total++;
}
实现每一份id不同,递增
只有一份,多的对象对该变量操作,会不断改变该变量
实现自动赋值
单例 (Singleton)设计模式
设计模式
是在大量的实践中总结和理论化之后优选的代码结构、编程风格、 以及解决问题的思考方式。设计模免去我们自己再思考和摸索。
就像是经典 的棋谱,不同的棋局,我们用不同的棋谱。”套路”
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,
并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,
我们首先必须将类的构 造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生 类的对象了,
但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,
静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。
/*
- 单例设计模式:
-
- 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
-
- 如何实现?
- 饿汉式 vs 懒汉式
-
- 区分饿汉式 和 懒汉式
- 饿汉式:
-
坏处:对象加载时间过长。
-
好处:饿汉式是线程安全的
- 懒汉式:好处:延迟对象的创建。
-
目前的写法坏处:线程不安全。--->到多线程内容时,再修改
- 单例模式的优点:
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的 产生需要比较多的资源时,
如读取配置、产生其他依赖对象时,则可 以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方 式来解决。
单例(Singleton)设计模式-应用场景
static 修饰的属性,相较于实例变量,有哪些特别之处(>=3点)
随着类的加载而加载;早于对象的创建;只要权限允许,可以通过”对象.static属性”的方式进行调用;存在于方法区的静态域
main方法:
public static void main(String[] args) {
}
/*
- main()方法的使用说明:
-
- main()方法作为程序的入口
-
- main()方法也是一个普通的静态方法
-
- String[] args 形参 使得main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
*/由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是 public,又因为Java虚拟机在执行main()方法时不必创建对象,
所以该方法必须 是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令 时传递给所运行的类的参数。
又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创 建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,
这种情 况,我们在之前的例子中多次碰到。
静态方法 不能调用非静态方法/属性
这也就是我们平常静态方法调用非静态的属性/方法时,解决方法之一就是补上静态
解释为什么加static
main()方法是入口,因此造对象又得再里边造,没有对象,方法要调得拿当前类去调。
没有对象,因此必须得是个静态的,用该类去调
/*
- 类的成员之四:代码块(或初始化块)
-
- 代码块的作用:用来初始化类、对象
-
- 代码块如果有修饰的话,只能使用static.
-
- 分类:静态代码块 vs 非静态代码块
-
- 静态代码块
-
>内部可以有输出语句
-
>随着类的加载而执行,而且只执行一次
-
>作用:初始化类的信息
-
>如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
-
>静态代码块的执行要优先于非静态代码块的执行
-
>静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
-
- 非静态代码块
-
>内部可以有输出语句
-
>随着对象的创建而执行
-
>每创建一个对象,就执行一次非静态代码块
-
>作用:可以在创建对象时,对对象的属性等进行初始化
-
>如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
-
>非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
/
/
- 对属性可以赋值的位置:
- ①默认初始化
- ②显式初始化/⑤在代码块中赋值
- ③构造器中初始化
- ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
- 执行的先后顺序:① - ② / ⑤ - ③ - ④
*/
在构造对象的时候会先运行初始化块,再运行构造器。静态初始化块在类加载的时候执行,用于初始化静态域。
非静态代码块得new对象的时候才执行
/*
- final:最终的
-
- final可以用来修饰的结构:类、方法、变量
-
- final 用来修饰一个类:此类不能被其他类所继承。
-
比如:String类、System类、StringBuffer类
String类功能已经完整,不想被扩充
-
- final 用来修饰方法:表明此方法不可以被重写
-
比如:Object类中getClass();
-
- final 用来修饰变量:此时的"变量"就称为是一个常量
-
4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化 ( 在方法里赋值初始化不靠谱)
-
4.2 final修饰局部变量:
-
尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值
-
以后,就只能在方法体内使用此形参,但不能进行重新赋值。
-
static final 用来修饰属性:全局常量
*/
native 关键字
使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非
Java 语言实现的,并且被编译成了 DLL,由 java 去调用。
/*
父类设计得非常抽象
以至于后来我们以后决定父类不再造对象
加关键字 abstract
- abstract关键字的使用
- 1.abstract:抽象的
- 2.abstract可以用来修饰的结构:类、方法
-
- abstract修饰类:抽象类
-
> 此类不能实例化
-
> 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
-
> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
*4. abstract修饰方法:抽象方法
-
> 抽象方法只有方法的声明,没有方法体(不让创建对象去调,则将该类声明成abstract类)
-
> 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
-
> 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
-
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
*/ //子类继承需要重写抽象父类的所有抽象方法否则子类需要声明为抽象类
问题:一个抽象类中可以定义构造器吗?
可以, 虽然自己不能造对象,子类还需要用父类的构造器,还需要加载父类的属性方法,一些非抽象,非静态的方法。
应用:
//父类方法体不知道如何具体的时候,可以定义为抽象类,抽象方法。子类只需要,重写父类所有的方法
抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提 供具体实现的对象的类。
*/
/*
- abstract使用上的注意点:
- 1.abstract不能用来修饰:属性、构造器等结构 构造器可以重载,不能重写
- 2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类。
接 口:概述
一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方
法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又 没有is-a的关系,
仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打 印机、扫描仪、摄像头、充电器、MP3机、手机、数码相机、移动硬盘等都 支持USB连接。
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要...则 必须能...”的思想。继承是一个"是不是"的关系,而接口实现则是 "能不能" 的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
解决 单继承 弊端
接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
用interface来定义。
接口中的所有成员变量都默认是由public static final修饰的。
接口中的所有抽象方法都默认是由public abstract修饰的。
接口中没有构造器。
接口采用多继承机制
/*
- 接口的使用
- 1.接口使用interface来定义
- 2.Java中,接口和类是并列的两个结构
- 3.如何定义接口:定义接口中的成员
-
3.1 JDK7及以前:只能定义全局常量和抽象方法
-
>全局常量:public static final的.但是书写时,可以省略不写
-
>抽象方法:public abstract的
-
3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
-
- 接口中不能定义构造器的!意味着接口不可以实例化
那如何实例化???
-
- Java开发中,接口通过让类去实现(implements)的方式来使用. (类和接口的关系,类去实现接口)
- 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
-
- Java类可以实现多个接口 --->弥补了Java单继承性的局限性
- 格式:class AA extends BB implements CC,DD,EE
-
- 接口与接口之间可以继承,而且可以多继承
-
- 接口的具体使用,体现多态性
-
- 接口,实际上可以看做是一种规范
- 面试题:抽象类与接口有哪些异同?
抽象类和接口有哪些共同点和区别?
相同点:不能实例化,都可以被继承
不同点:抽象类:有构造器。 接口:不能声明构造器
多继承vs 单继承
接口没有构造器,不可以被实例化。
抽象类也不能实例化,但是抽象类有构造器,让子类new的时候用的,子类对象实例化的时候,接口参与度很低
super调父类构造器起作用,对调用接口不起作用
6.6 接 口
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
一个类可以实现多个接口,接口也可以继承其它接口。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实
例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲, 接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义 (JDK7.0及之前),而没有变量和方法的实现。
类实现接口 1.要想实例化得覆盖实现所有的方法
2否则还是抽象类 abstract
实现某某接口 拥有某某功能
类 和 接口 实现
类 和 类 继承。 单继承
接口 和 接口 继承 可以多继承
类 和 接口 是并列的结构
接口:
接口的 --- 实现类 --- 对象
非匿名实现类的非匿名对象
非匿名实现类的匿名对象
匿名实现类的非匿名对象
匿名实现类的匿名对象
Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完
Java 8中关于接口的改进 全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行 其方法体。
我们经常在相互一起使用的类中使用静态方法。
你可以在标准库中 找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认 方法。
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
知识点:
知识点1: 接口中定义的静态方法,只能通过接口来调用。实现类看不到
知识点2:通过实现类的对象,可以调用接口中的默认方法。
如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
/*
- 类的内部成员之五:内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内 部的完整的结构又只为外部事物提供服务,
那么整个内部的完整结构最好使 用内部类。
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者 称为外部类。
Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完 整的名称。
Inner class的名字不能与包含它的外部类类名相同;
分类: 成员内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类
-
- Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
- 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
- 3.成员内部类:
-
一方面,作为外部类的成员:
-
>调用外部类的结构
-
>可以被static修饰
-
>可以被4种不同的权限修饰(可以被protect 、private)
-
另一方面,作为一个类:
-
> 类内可以定义属性、方法、构造器等
-
> 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
-
> 可以被abstract修饰
- 4.关注如下的3个问题
- 4.1 如何实例化成员内部类的对象
非静态的需要先有外部类的实例 - 4.2 如何在成员内部类中区分调用外部类的结构
class Person{
String name = "小明";
int age;
class Bird{
String name = "杜鹃";
public Bird(){
}
public void sing(){
System.out.println("我是一只小小鸟");
Person.this.eat();//调用外部类的非静态属性
eat();
System.out.println(age);
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
}
-
4.3 开发中局部内部类的使用
方法中局部内部类
返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){//创建一个实现了Comparable接口的类:局部内部类
//方式一:
class MyComparable implements Comparable{@Override public int compareTo(Object o) { return 0; }
}