(JAVA)类与对象(进阶)
目录
一、包
1. 包的简介
- 每个包对应一个文件夹,文件夹里还可以有子类文件夹,就是子包。
- 当你需要用到包的概念时,需要在
.java
文件的第一条语句,声明该文件是在什么包下
packagelearn.java_.test;publicclassTest{}
2. 包的使用
- 这时候可能有朋友疑问了:为什么不把文件路径写全呢?我的理解:你声明该文件的第一个包名为
learn
,那么在当你需要使用Test
类时,只需要把文件夹learn
直接复制到你使用的位置,就可以使用了。因为前面没写的部分,会默认在你使用的当前目录下去找learn\java_\test
。 - 示例:
importlearn.java_.test.Test;publicclass paper {publicstaticvoidmain(String[] args){Test t1 =newTest();}}
在这里,在paper类中使用
import learn.java_.test.Test;
来引包,会在paper.java所在目录下即src中找到
learn\java_\test
下的Test.java文件。当然如果把.Test改为.*是引入该test包下所有的文件。
- 这里思考一下,既然上面示例中import会找到Test类,那么为什么我们还要再声明
package learn.java_.test;
呢?因为他们的先后顺序不要搞错了,其实只有当你在该Test.java文件的第一条语句声明package learn.java_.test;
后,才能在paper类中import learn.java_.test.Test;成功。因为如果在该Test.java
文件没有使用package语句声明类所在的包时,java默任包的路径为当前文件夹,例如:在paper类中使用Test类,显然paper类的当前文件夹src中并没有Test类,因此会报错的。 - 在同一包中的类默认情况下可以互相访问。
二、🐯继承
1. 简介
- 一个子类只能有一个父类,而一个父类可以有多个子类。
- 父类实际上是所有子类的公共成员的集合。
- 被继承的类称为父类或者超类,由继承而得到的类称为子类。
- 使用关键字
extends
,示例://A继承了BpublicclassAextendsB{}classB{}
2. 使用
- 子类会继承父类可访问的成员变量和方法,可以直接通过子类对象来调用。
- 示例:
publicclassAextendsB{publicstaticvoidmain(String[] args){A a =newA();//访问父类的成员System.out.println(a.b); a.b();}}classB{int b =0;publicvoidb(){System.out.println("B");}}
3. super
super
是在子类方法中使用,来代表所继承的父类的引用。- 用于在子类方法中访问父类的属性、方法,但不能访问
private
所修饰的父类成员。 - 使用方法:super.变量名; super.方法;示例:
publicclassAextendsB{voidinB(){System.out.println(super.b);super.isB();}}classB{int b;voidisB(){}}
4. 构建方法
- 在执行子类的构造方法前,会先自动调用父类的无参构造方法。
- 如果父类没有无参构造器,则必须在子类的构造器中的第一条语句使用
super
来初始化父类 示例:publicclassAextendsB{publicA(int b){//调用B中的public B(int b)super(b);}}classB{int b;publicB(int b){this.b = b;}}
- 使用方法:super(参数列表)
5.🐻方法重写
- 也称方法覆盖(override)
- 在子类中如果有一个方法,其方法名、返回类型、参数都和父类中的某一方法一样,那么该子类方法就重写了父类同名方法的功能
- 示例:
publicclassAextendsB{//重写了B的say方法voidsay(){System.out.println("A");}}classB{voidsay(){System.out.println("B");}}
- 重写的方法的返回类型可以和父类方法一样,或者是父类返回类型的子类
- 👿重写的方法的访问权限可以比父类的大,但是不能比父类的小。如:示例中第3行省略的访问修饰符可以改成public,但是不能是protected。
- 子类中不能重写父类中有
final
和static
所修饰的方法。
对于成员变量没有重写的概念。
6. 对象的多态
- 一个对象的编译类型和运行类型可以不一致
- 编译类型是在定义时变量名前的类型,不能改变
- 运行类型是创建对象时
new
的类型,可以更改。 - 如:
publicclassAextendsB{publicstaticvoidmain(String[] args){B b =newA();}}classB{}
对象b
的编译类型是B
,而运行类型是A
。
6.1 向上转型
- 父类的引用指向了子类的对象,父类类型 引用名 = 子类对象
- 如:
publicclassAextendsB{publicstaticvoidmain(String[] args){//父类的引用指向了子类的对象B b =newA(); b =newC();}}classB{}classCextendsB{}
- 将子类对象,看作父类对象。因此该变量只能访问父类的成员,对于子类重写的方法,有覆盖的作用在。
6.2 向下转型
- 解决向上转型后,不能访问子类特有的成员。通过向下转型,来将父类对象通过强制转换为子类类型。
- 子类类型 引用名 = (子类类型)父类引用;
publicclassTest{publicstaticvoidmain(String[] args){B b =newA();System.out.println(b.boy);//向下转型System.out.println(((A)b).apple );A a =(A) b;System.out.println(a.apple);}}classAextendsB{int apple =1;}classB{int boy =0;}
6.3 instanceof
对象运算符 instanceof
- 使用:引用名 instanceof 类型
- 作用:如果该引用的对象的运行类型是该类型或该类型的子类型,返回
true
,否则返回false
。
7. 🔹练习
下面代码执行的结果是什么?
publicclassTest{publicstaticvoidmain(String[] args){B b =newA();System.out.println(b.sum());System.out.println(b.sum1());}}classAextendsB{int i =20;publicintgetI(){return i;}}classB{int i =10;publicintsum(){returngetI()+10;}publicintsum1(){return i+10;}publicintgetI(){return i;}}
结果:
- 对于b.sum(),首先访问到
class B
中的sum()
方法,在sum()
方法中有一个getI()
方法,对于getI()
的访问,其实在向下引用中,因为class A
中的getI()
是方法重写,该子类方法就重写了父类同名方法的功能,因此调用的是class A
中的getI()
,返回 20 20 20。因此sum()
的结果是 20 + 10 = 30 20+10 = 30 20+10=30。 - 对于b.sum1(),就直接是 10 + 10 = 20 10+10 = 20 10+10=20。因为成员方法没有重写的概念
8. Object类
- Object类是
java.lang
类库中的一个类,所有的类都是直接或间接继承该类。即Object类是所有类的源 - 如果一个类没有使用
extends
关键字,该类就默认为Object类
的子类。
8.1 equels()
该equels()是Object类中所定义的方法,而Object类是所有类的父类,因此任何类都可以直接使用该方法。
- 声明:public boolean equals(Object obj)
- 作用:判断两个引用所指向的是否为同一个对象
- 示例:
- 判断方法作用==可以判断基本类型(值是否相等)、引用类型(是否为同一对象[地址])equals()判断引用类型(是否为同一对象[地址])
- 方法重写如:在
java.lang.String
类中,就将equals()
进行方法重写如果String
类型的对象使用equals()
,则判断的是字符串的值。因为子类方法重写了equles()的功能。
8.2 toString()
同样也是
java.lang.Object
类中定义的方法,可以直接使用
- 声明:public String toString()
- 默认返回:全类名+@+哈希值的十六进制
- 当输出一个对象时,toString()方法会被默认调用。
- 示例:
- 子类往往会重写
toString()
,在IDEA中,可以使用快捷键:alt+insert
然后选择toString(),默认的是把属性的值输出。
9. final
9.1 简介
- final做为修饰符,表明最终的意思,即不可修改。
9.2 属性
- 如果用final来修饰成员变量,则说明该成员变量为常量。必须有初始化,且不能修改了
- 对其初始化有三种方式:
//方式1:在定义时初始化classA{finalint a =10;}//方式2:在构造器中初始化classB{finalint b;B(int b){this.b = b;}}//方式3:在代码块中初始化classC{finalint c;{ c =10;}}
9.3 方法
- 如果用final来修饰成员方法,则表明该方法不能被子类所重写,可提高安全性
9.4 类
- 如果用final来修饰类,则该类不能继承,既不能做为父类,也不能做为子类。
四、抽象类
1.简介
- 使用修饰符abstract所修饰的类
- 抽象类是专门作为父类而创建的,它的作用类似于__模板__
- 目的是根据抽象类的格式来创建子类,再由其子类来创建对象。
- 因此抽象类不能创建实例化对象(new)
2.💫抽象方法
- 使用修饰符abstract所修饰的方法
- 使用:[其他修饰符] abstract 返回类型 方法名(参数列表);抽象方法没有{}(方法体)
- 示例:
//抽象类BabstractclassB{//抽象方法boy()publicabstractvoidboy();}
- 抽象类中可以没有抽象方法,但是有抽象方法的类必须声明为抽象类
- 抽象类的子类必须实现父类中的所有抽象方法,或者子类也是一个抽象类示例:
abstractclassB{publicabstractvoidboy();}classAextendsB{//实现父类中的抽象类publicvoidboy(){}}//子类C也是一个抽象类abstractclassCextendsB{}
- 抽象方法不能使用private、final、static来修饰,因为该关键字修饰的方法都不能重写。
3.其他
- abstract只能用来修饰类和方法
- 普通类有的成员,抽象类都可以有,因为抽象类的本质还是类只是不能实例化对象
五、接口
1.简介
- 使用关键字interface来定义接口
//接口的定义[public/无]interface 接口名{}
- 接口也不能创建实例化对象(new)
2.🎏接口的成员
- 属性,实际上是静态常量
interfaceA{//属性a,必须初始化int ONEA =100;}
对于接口的属性都是public static final修饰的,当省略修饰符,系统会默认。 如:int a=100;
实际上是public static final int a=100;
。 - 抽象方法
interfaceA{//抽象方法apple()voidapple();}
对于抽象方法,当省略修饰符,系统会默认为public abstract。如:上例,实际上是public abstract void apple();
- 在定义接口时,一般都省略属性和抽象方法的修饰符
- 静态方法
interfaceA{//静态方法staticvoidgo(){System.out.println("接口A中的静态方法");}}
接口中的静态方法,不能被子接口和实现类所继承,但可以通过**接口名.静态方法名()**来访问 - 默认方法
interfaceA{//默认方法defaultvoidto(){System.out.println("接口A中的默认方法");}}
接口中的默认方法用default
修饰符定义,为达到一种在接口中定义与类中普通成员方法相似的效果。 - 接口中的方法都是
public
的,可以在定义时省略。
3.接口的实现
- 利用接口来创建新类的过程是接口的实现
- 接口的实现类似于继承,只不过是使用关键字implenments来实现接口
- 普通类实现接口必须实现接口所有的抽象方法,而抽象类则可不必。
//接口AinterfaceA{voidapple();}//类B实现接口AclassBimplementsA{//实现抽象方法apple()publicvoidapple(){}}//抽象类C实现接口AabstractclassCimplementsA{}
- 因为接口中的方法都是
public
,所有在实现抽象方法时,需要显示的使用public
修饰符,实现的方法不能缩小接口中该方法的访问控制范围(和方法重写一样)。
3.1用接口实现类的多重继承
- java不支持类的多重继承,但是可以利用接口间接的解决这个问题
- 一个类可以实现多个接口,它们间用
,
分隔interfaceA{}interfaceB{}classCimplementsA,B{}
- 接口中的常量、抽象方法和默认方法可以被实现该接口的类所继承。但不能继承接口中的静态方法。
3.2名字冲突
- 一般指一个类实现多个接口,不同接口中相同的默认方法引起的冲突
interfaceA{//常量appleint apple =0;//默认方法defaultvoidto(){}//抽象方法voidplay();}interfaceB{//常量appleint apple =10;//默认方法defaultvoidto(){}//抽象方法voidplay();}classCimplementsA,B{//重写to() //这里没有defaultpublicvoidto(){//A.super.to();}//实现play()publicvoidplay(){}voiduse(){System.out.println(A.apple);System.out.println(B.apple);}}
- 当实现到两个相同的默认方法时,可以通过重写来解决冲突。而对于抽象方法,因为后续也是要(实现)的,也会达到重写的效果,因此没有冲突。对于接口属性,可以通过接口名.属性来避免冲突。
4.接口的多态
- 用一个类实现一个接口后,该类和该接口的关系类似于继承。
- 如: 二、继承 6。对象的多态
- 可以使用接口的引用来指向子类对象。
publicclassTest{publicstaticvoidmain(String[] args){//父接口引用a指向子类对象A a =newB(); a.apple();}}interfaceA{voidapple();}classBimplementsA{//实现抽象方法apple()publicvoidapple(){}//B特有的boy()voidboy(){}}
- 也有相同的向下转型
B b =(B)a;b.boy();
5. 接口的继承
- 和类相似,接口也可以继承,使用extends来表明继承关系
- 一个接口可以继承多个父接口,它们间用
,
分隔interfaceA{}interfaceB{}//接口C继承接口A和BinterfaceCextendsA,B{}
- 接口会继承父接口中的常量、抽象方法和默认方法,但不能继承父接口中的静态方法。
- 接口不能和类通过extends产生关系。
- 如果遇到名字冲突问题可参考
2.接口的实现
中的解决方法
六、内部类
- 内部类是定义在类中的类,也称为嵌套类,包含内部类的类称为外部类。
- 内部类可以看作外部类的一个成员,与一般类相同,只是定义置于一个类的内部。
1.分类
- 定义在外部类局部位置上,相当于局部变量的地位a.局部内部类(有类名)b.匿名内部类(没类名)
- 定义在外部类的成员位置,相当于类成员的地位a.成员内部类(无static)b.静态内部类(有static)
2. 局部内部类
- 示例:
classOuter{//外部类int a =1;voidOutf(){}publicvoidOutf1(){//一般在方法里定义classInner{//内部类int a =2;publicvoidInf(){//可以直接访问外部类的成员Outf();//Outer.this本质就外部类的对象System.out.println(Outer.this.a);//1//就近原则System.out.println(a);//2}}//使用:Inner inner =newInner();inner.Inf();}}
- 在内部类里可以直接访问外部类的所有成员,对于成员重名问题,默认遵循就近原则,然后如果是访问外部类的成员,可以使用外部类名.this.成员来访问
- 不能有访问修饰符(public、private和protected)。因为它相当于一个局部变量
- 作用域:在定义它的方法或代码块的{}范围中
- 使用:在作用域的{}范围内,先创建对象,再访问。
3.🔑匿名内部类
- 如果某个类的对象只用一次,则可在定义类的同时就创建该类的对象。
- 这种定义类的方法不取名字,所以称为匿名内部类,类名由系统分配。
- 语法:返回的是一个对象的引用
new 类/接口(参数列表){ 类体};
- 其中类/接口,是匿名内部类所继承的类或实现的接口,类也可以是抽象类,但都只能用一个。
- 示例:
classOuter{//外部类int a =-1;publicvoidf(){//定义匿名内部类//(100)会调用父类的构造器Father father =newFather(100){//方法重写publicvoidshow(){System.out.println(Outer.this.getClass()+" a="+Outer.this.a);System.out.println(this.getClass()+" a="+a);}};//使用该匿名类的对象 father.show();}}classFather{int a;//构造器publicFather(int a){this.a = a;}publicvoidshow(){}}
- 可以发现
father
的运行类型是class Outer$1
这就是系统自动分配的匿名内部类的类名 - 匿名内部类也可以直接访问外部类的所有成员。如果名字相同,遵循就近原则,此时访问外部类成员可以使用外部类名.this.成员
- 作用域: 在定义它的方法或代码块的范围中
- 不能有修饰符,因为相当于一个局部变量。不能定义构造器,因为没有名字。
- 匿名内部类,可以当做实参传递
publicclassTest{publicstaticvoidmain(String[] args){A a =newA();//直接传递一个实现接口B的匿名内部类a.Go(newB(){//实现抽象方法go()publicvoidgo(){System.out.println("go");}});}}classA{publicvoidGo(B b){ b.go();}}interfaceB{voidgo();//抽象方法}
4. 成员内部类
- 示例:
classOuter{//外部类int a =100;publicvoidOutf(){}//成员内部类,定义在外部类成员位置classInner{int a =9;publicvoidInf(){//可直接访问外部类的成员Outf();System.out.println(Outer.this.a);}}publicvoidOutf1(){//使用成员内部类Inner inner =newInner();System.out.println(inner.a);}}
- 在外部类外使用该内部类
publicclassTest{publicstaticvoidmain(String[] args){//创建外部类对象Outer outer =newOuter();//创建该外部类对象的内部类对象Outer.Inner inner =outer.newInner();}}
- 成员内部类可以直接可以直接访问外部类的所有成员。如果名字相同,遵循就近原则,此时访问外部类成员可以使用外部类名.this.成员
- 可以添加访问修饰符(public、默认、protected、private),相当于一个成员
5. 静态内部类
- 示例:
classOuter{//外部类staticint a =100;staticint b =10;publicvoidOutf(){}//静态内部类staticclassInner{staticint b =9;voidInf(){//可直接访问外部类的静态成员System.out.println(a);//如果重名,就近原则,(外部类名.成员)System.out.println(b);System.out.println(Outer.b);//对于外部类的非静态成员的访问Outer outer =newOuter();outer.Outf();}}//静态内部类的使用publicvoidOutf1(){//静态内部类的静态成员System.out.println(Inner.b);//非静态成员,创建对象,再访问Inner inner =newInner();inner.Inf();}}
- 在外部类外访问该静态内部类
publicclassTest{publicstaticvoidmain(String[] args){//访问静态内部类的静态成员System.out.println(Outer.Inner.b);//访问静态内部类的非静态成员:先创建,在访问Outer.Inner inner =newOuter.Inner();inner.Inf();}}
- 成员内部类可以直接可以直接访问外部类的静态成员。如果名字相同,遵循就近原则,此时访问外部类静态成员可以使用外部类名.成员
- 可以添加访问修饰符(public、默认、protected、private),相当于一个成员
七、枚举
1.简介
- 对于某一个变量只有几种固定取值时,常声明为枚举类型。如:季节。
- 语法:使用关键字enum声明枚举类型
[修饰符]enum 枚举类型名 { 枚举成员 方法}
- 枚举成员也称枚举常量或枚举值,不能重名,用
,
分隔,都默认被final public static修饰 - 这里枚举类型名,也是枚举成员的数据类型,所有枚举成员也称为枚举实例或枚举对象
- 枚举是一种特殊的类,会默认继承
java.lang.Enum
类,因此不能再继承其他类,但可以实现接口。 - 非抽象的枚举会默认使用
final
修饰,因此不能派生子类 - 枚举的所有枚举成员必须放在枚举体的第一条语句。
2.不包含方法的枚举
- 因为自动继承
Enum
类,所以可以直接使用Enum
类中的方法 - 方法功能public static enumtype[] values()返回枚举类型的数组,包含所有枚举成员,按声明顺序存储public static enumtype valueOf(String str)返回名称为str的枚举成员public String toString()返回枚举成员的名称public final int ordinal()返回枚举成员的序号(从0开始)public final String name()返回枚举常量的名称…………
3. ✨包含属性和方法的枚举
- 如果枚举成员,需要有更丰富的内容,这时就可以赋予他们属性和方法
- 示例:
//定义枚举类型SeasonenumSeason{//声明4个枚举成员SPRING("春"),SUMMER("夏"),AUTUMN("秋"),WINTET("冬");//属性privateString name;//构造器,默认是privateSeason(String name){this.name = name;}//重写toString()publicStringtoString(){return name;}voidplay(){System.out.println(name+"天就要去玩耍");}}
- 枚举的构造方法只能是
private
修饰的,可以省略。 - 因此枚举成员必须在定义时就加上
(参数列表)
,如果有无参构造器,则可省略()
🦀🦀观看。
待续~~
版权归原作者 异样旧日 所有, 如有侵权,请联系我们删除。