面向对象高级
继承
继承的基本概念及实现、类的继承格式、类图
继承:子类继承父类,可以扩展已有类的功能。
为什么要有继承?使用继承的优点
可以把父类的内容在子类继续使用,这样一来,子类如果有一些重复的方法就不用重新定义了。
子类也称为派生类。
继承的限制
java中只允许单继承,不允许多重继承。即:一个类只能继承一个父类。
但是允许多层继承。即:一个子类可以有一个父类,一个父类还可以有一个父类。
单继承:一个孩子只能有一个父亲。
不能多重继承,但是可以多层继承。
类图
访问限制
在使用继承的时候应该注意:子类是不能直接访问父类中的私有成员的,但是子类可以调用父类中的非私有方法,但是不能直接调用父类中的私有成员。
总结:
1,继承的主要目的:扩展类的功能。
2,java中一个子类只能继承一个父类。
3,java中不允许多重继承,但是允许多层继承。
子类对象的实例化过程
子类对象在实例化之前必须首先调用父类中的构造方法之后再调用子类自己的构造方法。
实例化子类对象的时候,虽然没有明确写代码去调用父类构造,但是还是会去默认的调用父类的构造方法。super()
默认隐藏。
方法覆写的概念及其实现
方法覆写就是指子类中定义了与父类中同名的方法,但是在方法覆写中必须考虑到权限,即:被子类覆写的方法不能拥有比父类方法更加严格的访问权限,否则程序会无法编译。
方法被覆写之后,子类对象调用同名方法,调用的肯定是子类中覆写过的方法。
如果方法没有被覆写,子类对象调用该方法,则会去调用从父类继承过来的方法,也就是父类中定义的方法。
如果子类将父类的方法覆写了,子类对象调用的时候调用的是子类中覆写过的方法,那么,在覆写之后能否调用父类中的方法呢?当然是可以的:可以通过super关键字可以完成。
super关键字 ,可以实现从子类对象访问父类中的内容。如果子类对象要访问父类中被覆写过的方法需要:super.方法()
。
super可以明确表示某个方法是从父类中继承过来的。调用方法前面加super,表示后面的方法直接从父类中去找;不加super默认会首先从子类中去找这个方法,如果找不到才会去父类中去找。
使用super只是更加明确的说明直接从父类中去找方法,不需要去子类中去查找调用了。
父类中private方法是没办法被覆写的,就算子类中定义同名方法,扩大权限,子类也是继承不到父类中的private方法的。 方法并没有被覆写,此时相当于在子类中重新定义了一个新的方法出来,这根本不叫覆写。
属性的覆盖:
子类中声明了与父类中同名,访问权限不小于父类的同名属性。
可以通过this调用本类中的属性,可以通过super调用父类中的属性。
方法的覆写与重载的区别
super关键字的作用
this关键字表示本类。
super关键字表示父类。从子类调用父类的指定操作,比如:调用属性,方法,构造等。
子类实例化的时候会默认调用父类中的无参构造,如果希望调用有参构造,则必须在子类中明确声明。
父类中没有无参构造则需要在子类中明确调用父类的有参构造,默认只能去找无参构造,当明确定义了有参构造之后,就没有无参构造了。
class Person{ // 定义Person类
private String name ; // 定义name属性
private int age ; // 定义age属性
public Person(String name,int age){
this.setName(name) ;
this.setAge(age) ;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
public String getInfo(){
return "姓名:" + this.getName() + ";年龄:" + this.getAge() ;
}
};
class Student extends Person{ // 定义Student类
private String school ; // 定义school属性
public Student(String name,int age,String school){
super(name,age) ; // 明确调用父类中有两个参数的构造
this.school = school ;
}
public void setSchool(String school){
this.school = school ;
}
public String getSchool(){
return this.school ;
}
public String getInfo(){
return super.getInfo() + ";学校:" + this.getSchool() ;
}
};
public class SuperDemo01{
public static void main(String arsg[]){
Student stu = new Student("张三",30,"清华大学") ;// 实例化子类对象
System.out.println(stu.getInfo()) ;
}
};
不管任何时候,子类实例化的时候,永远都要先去调用父类中的构造方法,默认调用的是无参构造。
this与super区别
例子:
class Array{ // 表示数组
private int temp[] ; // 整型数组
private int foot ; // 定义添加位置
public Array(int len){
if(len>0){
this.temp = new int[len] ;
}else{
this.temp = new int[1] ; // 最少维持空间是1个
}
}
public boolean add(int i){ // 增加元素
if(this.foot<this.temp.length){ // 还有空间
this.temp[foot] = i ; // 增加元素
this.foot ++ ;// 修改脚标
return true ;
}else{
return false ;
}
}
public int[] getArray(){
return this.temp ;
}
};
class SortArray extends Array{ // 排序类
public SortArray(int len){
super(len) ;
}
public int[] getArray(){ // 覆写方法
java.util.Arrays.sort(super.getArray()) ; // 排序操作
return super.getArray() ;
}
};
class ReverseArray extends Array{ // 反转操作类
public ReverseArray(int len){
super(len) ;
}
public int[] getArray() {
int t[] = new int[super.getArray().length] ; // 开辟一个新的数组
int count = t.length - 1 ;
for(int x=0 ;x<t.length;x++){
t[count] = super.getArray()[x] ; // 数组反转
count-- ;
}
return t ;
}
};
public class ArrayDemo{
public static void main(String args[]){
// ReverseArray a = null ; // 声明反转类对象
// a = new ReverseArray(5) ; // 开辟5个空间大小
SortArray a = null ;
a = new SortArray(5) ;
System.out.print(a.add(23) + "\t") ;
System.out.print(a.add(21) + "\t") ;
System.out.print(a.add(2) + "\t") ;
System.out.print(a.add(42) + "\t") ;
System.out.print(a.add(5) + "\t") ;
System.out.print(a.add(6) + "\t") ;
print(a.getArray()) ;
}
public static void print(int i[]){ // 输出数组内容
for(int x=0;x<i.length;x++){
System.out.print(i[x] + "、") ;
}
}
};
final关键字
终结器。
在一个方法中定义了内部类,如果此方法的参数要被该内部类访问到,则此参数必须用final声明。
final关键字的使用要求
final 关键字可以声明类、属性、方法:
1,使用final
声明的类不能有子类; (太监类)
2,使用final
声明的方法不能被子类所覆写;
3,使用final
声明的变量即为常量,常量的值不可修改;而且常量在声明的时候必须明确为其指定内容,否则编译报错。常量属性一般全部字母使用大写。
使用static final
声明的常量称为全局常量。因为static
声明的内容是所有对象所共同拥有的。
抽象类
抽象类的作用、定义格式、使用规则
包含一个抽象方法的类就称为抽象类;
之声明而未实现的方法称为抽象方法,抽象方法必须用abstract
关键字声明。
抽象类使用规则:
1,包含一个抽象方法的类必须是抽象类;
2,抽象类和抽象方法都要使用abstract
关键字声明;
3,抽象方法只需声明而不需要实现;
4,抽象类必须被子类继承,子类(如果不是抽象类)必须覆写抽象类中的全部抽象方法;
抽象类和普通类虽然非常相似,相比只是多了一个抽象方法,而且抽象类也使用了abstract
声明,但是抽象类不能直接实例化。
抽象类必须有子类,子类必须覆写抽象类中的全部抽象方法,前提是这个子类是一个普通类。
1,一个抽象类可以使用final关键字声明么
final声明的类是不能有子类的;
抽象类是必须有子类的;
这是矛盾的,所以final和abstract是不能同时声明一个类的;
所以,一个抽象类不可以使用final声明。
2,一个抽象类中可以定义构造方法么
首先,答案是可以的。
虽然抽象类不能直接实例化,但是在抽象类中是允许存在构造方法的,因为抽象类依然使用的是类的继承关系,符合子类对象的实例化过程,而且抽象类中也存在各个属性,所以子类在实例化之前肯定是先要对父类进行实例化的。
接口
接口的定义格式及使用
接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法组成。
如果一个类完全是由全局常量(static final声明)和public抽象方法组成,就可以将其定义成一个接口。
对于接口来说,因为其在定义的时候已经定义了接口的组成只有全局常量和抽象方法,所以开发中可以简化定义,接口的抽象方法的public abstract
关键字可以省去,全局常量的public static final
可以省去,因为接口中的方法,只能是public的,所以可以省去。省去的关键字在编译的时候会自动加入。
interface A{ // 定义接口A
public static final String AUTHOR = "李华" ; // 全局常量
public abstract void print() ; // 抽象方法
public abstract String getInfo() ; // 抽象方法
}
interface B{ // 定义接口B
String AUTHOR = "李华" ; // 全局常量
void print() ; // 抽象方法
String getInfo() ; // 抽象方法
}
以上代码,接口A
和接口B
的两种定义方式完全是一样的效果,没有任何区别。因为接口的所有方法都是public
的,都是abstract
的;接口的所有常量都是全局常量,都是 public static final
的。但是抽象类是不可以省去这些关键字的,省去了的话在程序编译的时候就会报错。
问题:
接口中的方法可以定义为protected
的或者default
的么?
首先,答案是不可以定义为protected
的,至于default
,就是什么访问权限都不加,但是在接口中什么都不加其实就是public
的,只是语法简化没有明文显示出public
,但是编译阶段编译器会自动加上该访问权限修饰符。
实现接口、接口与抽象类的关系
与抽象类一样,接口要使用也必须通过子类,子类必须覆写全部抽象方法,接口通过implements关键字实现接口。
一个接口的子类可以同时实现多个接口,但是一个抽象类的子类只能继承一个抽象类。
如果接口的子类不是抽象类的话,则需要覆写抽象类中的全部抽象方法。
抽象类可以实现接口,但是接口不能继承抽象类。但是一个接口可以继承多个接口,实现接口的多继承。
interface A{ // 定义接口A
public String AUTHOR = "李华" ; // 全局常量
public void print() ; // 抽象方法
public String getInfo() ; // 抽象方法
}
interface B{ // 定义接口B
public void say() ; // 定义抽象方法
}
class X implements A,B{ // X类同时实现A和B两个接口
public void say(){
System.out.println("Hello World!!!") ;
}
public String getInfo(){
return "HELLO" ;
}
public void print(){
System.out.println("作者:" + AUTHOR) ;
}
};
public class InterfaceDemo03{
public static void main(String args[]){
X x = new X() ; // 实例化子类对象
x.say() ;
x.print() ;
}
};
interface A{ // 定义接口A
public String AUTHOR = "李华" ; // 全局常量
public void print() ; // 抽象方法
public String getInfo() ; // 抽象方法
}
abstract class B{ // 定义抽象类B
public abstract void say() ; // 定义抽象方法
}
class X extends B implements A{ // X类线继承B类,再实现A接口
public void say(){
System.out.println("Hello World!!!") ;
}
public String getInfo(){
return "HELLO" ;
}
public void print(){
System.out.println("作者:" + AUTHOR) ;
}
};
public class InterfaceDemo04{
public static void main(String args[]){
X x = new X() ; // 实例化子类对象
x.say() ;
x.print() ;
}
};
interface A{ // 定义接口A
public String AUTHOR = "李华" ; // 全局常量
public void print() ; // 抽象方法
public String getInfo() ; // 抽象方法
}
abstract class B implements A{ // 定义抽象类B,实现接口A
public abstract void say() ; // 定义抽象方法
}
class X extends B{ // X类线继承B类
public void say(){
System.out.println("Hello World!!!") ;
}
public String getInfo(){
return "HELLO" ;
}
public void print(){
System.out.println("作者:" + AUTHOR) ;
}
};
public class InterfaceDemo05{
public static void main(String args[]){
X x = new X() ; // 实例化子类对象
x.say() ;
x.print() ;
}
};
interface A{ // 定义接口A
public String AUTHOR = "李华" ; // 全局常量
public void printA() ; // 抽象方法
}
interface B{
public void printB() ;
}
interface C extends A,B{
public void printC() ;
}
class X implements C{ // X类实现C接口
public void printA(){
System.out.println("A、Hello World!!!") ;
}
public void printB(){
System.out.println("B、Hello MLDN") ;
}
public void printC(){
System.out.println("C、Hello LXH") ;
}
};
public class InterfaceDemo06{
public static void main(String args[]){
X x = new X() ; // 实例化子类对象
x.printA() ;
x.printB() ;
x.printC() ;
}
};
1,接口是一个特殊的类,只包含了全局常量和抽象方法,接口中的抽象方法可以不加abstract
public
都可以不加,因为接口的抽象方法必须是public
的,但是抽象类中的抽象方法必须加abstract
关键字声明。
2,一个类只能继承一个父类,但是可以同时实现多个接口。
3,一个接口可以同时继承多个接口,实现多继承。
4,接口和抽象类一样,都必须依靠子类。
5,一个抽象类可以实现多个接口,但是一个接口不可以继承抽象类。
相关
Java中抽象类和接口的介绍及二者间的区别
https://baijiahao.baidu.com/s?id=1675962405769465694&wfr=spider&for=pc