(JAVA)类与对象(进阶)
一、包
1. 包的简介
-
每个包对应一个文件夹,文件夹里还可以有子类文件夹,就是子包。 -
当你需要用到包的概念时,需要在.java 文件的第一条语句,声明该文件是在什么包下
package learn.java_.test;
public class Test {
}
2. 包的使用
-
这时候可能有朋友疑问了:为什么不把文件路径写全呢? 我的理解:你声明该文件的第一个包名为learn ,那么在当你需要使用Test 类时,只需要把文件夹learn 直接复制到你使用的位置,就可以使用了。因为前面没写的部分,会默认在你使用的当前目录下去找learn\java_\test 。 -
示例: import learn.java_.test.Test;
public class paper {
public static void main(String[] args) {
Test t1 = new Test();
}
}
在这里,在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 ,示例:
public class A extends B{
}
class B {
}
2. 使用
-
子类会继承父类可访问的成员变量和方法,可以直接通过子类对象来调用。 -
示例: public class A extends B{
public static void main(String[] args) {
A a = new A();
System.out.println(a.b);
a.b();
}
}
class B {
int b = 0;
public void b() {
System.out.println("B");
}
}
3. super
-
super 是在子类方法中使用,来代表所继承的父类的引用。 -
用于在子类方法中访问父类的属性、方法,但不能访问private 所修饰的父类成员。 -
使用方法:super.变量名; super.方法; 示例: public class A extends B{
void inB(){
System.out.println(super.b);
super.isB();
}
}
class B {
int b;
void isB(){}
}
4. 构建方法
-
在执行子类的构造方法前,会先自动调用父类的无参构造方法。 -
如果父类没有无参构造器,则必须在子类的构造器中的第一条语句使用super 来初始化父类 示例: public class A extends B{
public A(int b) {
super(b);
}
}
class B {
int b;
public B(int b) {
this.b = b;
}
}
-
使用方法:super(参数列表)
5.🐻方法重写
-
也称方法覆盖(override) -
在子类中如果有一个方法,其方法名、返回类型、参数都和父类中的某一方法一样,那么该子类方法就重写了父类同名方法的功能 -
示例: public class A extends B{
void say() {
System.out.println("A");
}
}
class B {
void say() {
System.out.println("B");
}
}
-
重写的方法的返回类型可以和父类方法一样,或者是父类返回类型的子类 -
👿重写的方法的访问权限可以比父类的大,但是不能比父类的小。 如:示例中第3行省略的访问修饰符可以改成public,但是不能是protected。 -
子类中不能重写父类中有final 和static 所修饰的方法。
对于成员变量没有重写的概念。
6. 对象的多态
6.1 向上转型
-
父类的引用指向了子类的对象,父类类型 引用名 = 子类对象 -
如: public class A extends B{
public static void main(String[] args) {
B b = new A();
b = new C();
}
}
class B {
}
class C extends B {
}
-
将子类对象,看作父类对象。因此该变量只能访问父类的成员,对于子类重写的方法,有覆盖的作用在。
6.2 向下转型
-
解决向上转型后,不能访问子类特有的成员。通过向下转型,来将父类对象通过强制转换为子类类型。 -
子类类型 引用名 = (子类类型)父类引用; public class Test{
public static void main(String[] args) {
B b = new A();
System.out.println(b.boy);
System.out.println( ((A)b).apple );
A a = (A) b;
System.out.println(a.apple);
}
}
class A extends B{
int apple = 1;
}
class B {
int boy = 0;
}
6.3 instanceof
对象运算符 instanceof
7. 🔹练习
下面代码执行的结果是什么?
public class Test{
public static void main(String[] args) {
B b = new A();
System.out.println(b.sum());
System.out.println(b.sum1());
}
}
class A extends B{
int i = 20;
public int getI() {
return i;
}
}
class B {
int i = 10;
public int sum() {
return getI()+10;
}
public int sum1() {
return i+10;
}
public int getI() {
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 属性
9.3 方法
- 如果用final来修饰成员方法,则表明该方法不能被子类所重写,可提高安全性
9.4 类
- 如果用final来修饰类,则该类不能继承,既不能做为父类,也不能做为子类。
四、抽象类
1.简介
- 使用修饰符abstract所修饰的类
- 抽象类是专门作为父类而创建的,它的作用类似于__模板__
- 目的是根据抽象类的格式来创建子类,再由其子类来创建对象。
- 因此抽象类不能创建实例化对象(new)
2.💫抽象方法
-
使用修饰符abstract所修饰的方法 -
使用:[其他修饰符] abstract 返回类型 方法名(参数列表); 抽象方法没有{}(方法体) -
示例:
abstract class B {
public abstract void boy();
}
-
抽象类中可以没有抽象方法,但是有抽象方法的类必须声明为抽象类 -
抽象类的子类必须实现父类中的所有抽象方法,或者子类也是一个抽象类 示例: abstract class B {
public abstract void boy();
}
class A extends B{
public void boy() {
}
}
abstract class C extends B{
}
-
抽象方法不能使用private、final、static来修饰,因为该关键字修饰的方法都不能重写。
3.其他
五、接口
1.简介
2.🎏接口的成员
-
属性,实际上是静态常量 interface A {
int ONEA = 100;
}
对于接口的属性都是public static final修饰的,当省略修饰符,系统会默认。 如:int a=100; 实际上是public static final int a=100; 。 -
抽象方法 interface A {
void apple();
}
对于抽象方法,当省略修饰符,系统会默认为public abstract。 如:上例,实际上是public abstract void apple(); -
在定义接口时,一般都省略属性和抽象方法的修饰符 -
静态方法 interface A {
static void go(){
System.out.println("接口A中的静态方法");
}
}
接口中的静态方法,不能被子接口和实现类所继承,但可以通过**接口名.静态方法名()**来访问 -
默认方法 interface A {
default void to(){
System.out.println("接口A中的默认方法");
}
}
接口中的默认方法用default 修饰符定义,为达到一种在接口中定义与类中普通成员方法相似的效果。 -
接口中的方法都是public 的,可以在定义时省略。
3.接口的实现
-
利用接口来创建新类的过程是接口的实现 -
接口的实现类似于继承,只不过是使用关键字implenments来实现接口 -
普通类实现接口必须实现接口所有的抽象方法,而抽象类则可不必。
interface A {
void apple();
}
class B implements A{
public void apple() {
}
}
abstract class C implements A{
}
-
因为接口中的方法都是public ,所有在实现抽象方法时,需要显示的使用public 修饰符,实现的方法不能缩小接口中该方法的访问控制范围(和方法重写一样)。
3.1用接口实现类的多重继承
-
java不支持类的多重继承,但是可以利用接口间接的解决这个问题 -
一个类可以实现多个接口,它们间用, 分隔 interface A{}
interface B{}
class C implements A,B{}
-
接口中的常量、抽象方法和默认方法可以被实现该接口的类所继承。但不能继承接口中的静态方法。
3.2名字冲突
-
一般指一个类实现多个接口,不同接口中相同的默认方法引起的冲突 interface A{
int apple = 0;
default void to(){}
void play();
}
interface B{
int apple = 10;
default void to(){}
void play();
}
class C implements A,B{
public void to() {
A.super.to();
}
public void play() {
}
void use(){
System.out.println(A.apple);
System.out.println(B.apple);
}
}
-
当实现到两个相同的默认方法时,可以通过重写来解决冲突。而对于抽象方法,因为后续也是要(实现)的,也会达到重写的效果,因此没有冲突。对于接口属性,可以通过接口名.属性来避免冲突。
4.接口的多态
-
用一个类实现一个接口后,该类和该接口的关系类似于继承。 -
如: 二、继承 6。对象的多态 -
可以使用接口的引用来指向子类对象。 public class Test{
public static void main(String[] args) {
A a = new B();
a.apple();
}
}
interface A {
void apple();
}
class B implements A{
public void apple() {
}
void boy(){}
}
-
也有相同的向下转型 B b = (B)a;
b.boy();
5. 接口的继承
-
和类相似,接口也可以继承,使用extends来表明继承关系 -
一个接口可以继承多个父接口,它们间用, 分隔 interface A{
}
interface B{
}
interface C extends A,B{
}
-
接口会继承父接口中的常量、抽象方法和默认方法,但不能继承父接口中的静态方法。 -
接口不能和类通过extends产生关系。 -
如果遇到名字冲突问题 可参考2.接口的实现 中的解决方法
六、内部类
- 内部类是定义在类中的类,也称为嵌套类,包含内部类的类称为外部类。
- 内部类可以看作外部类的一个成员,与一般类相同,只是定义置于一个类的内部。
1.分类
-
定义在外部类局部位置上,相当于局部变量的地位 a.局部内部类(有类名) b.匿名内部类(没类名) -
定义在外部类的成员位置,相当于类成员的地位 a.成员内部类(无static) b.静态内部类(有static)
2. 局部内部类
-
示例: class Outer{
int a = 1;
void Outf(){}
public void Outf1(){
class Inner{
int a = 2;
public void Inf(){
Outf();
System.out.println(Outer.this.a);
System.out.println(a);
}
}
Inner inner = new Inner();
inner.Inf();
}
}
-
在内部类里可以直接访问外部类的所有成员,对于成员重名问题,默认遵循就近原则,然后如果是访问外部类的成员,可以使用外部类名.this.成员来访问 -
不能有访问修饰符(public、private和protected)。因为它相当于一个局部变量 -
作用域:在定义它的方法或代码块的{}范围中 -
使用:在作用域的{}范围内,先创建对象,再访问。
3.🔑匿名内部类
-
如果某个类的对象只用一次,则可在定义类的同时就创建该类的对象。 -
这种定义类的方法不取名字,所以称为匿名内部类,类名由系统分配。 -
语法:返回的是一个对象的引用 new 类/接口(参数列表) {
类体
};
-
其中类/接口,是匿名内部类所继承的类或实现的接口,类也可以是抽象类,但都只能用一个。 -
示例: class Outer{
int a = -1;
public void f(){
Father father = new Father(100){
public void show() {
System.out.println(Outer.this.getClass()+
" a="+Outer.this.a);
System.out.println(this.getClass()+" a="+a);
}
};
father.show();
}
}
class Father{
int a;
public Father(int a) {
this.a = a;
}
public void show(){}
}
-
可以发现father 的运行类型是class Outer$1 这就是系统自动分配的匿名内部类的类名 -
匿名内部类也可以直接访问外部类的所有成员。如果名字相同,遵循就近原则,此时访问外部类成员可以使用外部类名.this.成员 -
作用域: 在定义它的方法或代码块的范围中 -
不能有修饰符,因为相当于一个局部变量。不能定义构造器,因为没有名字。 -
匿名内部类,可以当做实参传递 public class Test{
public static void main(String[] args) {
A a = new A();
a.Go(new B(){
public void go() {
System.out.println("go");
}
});
}
}
class A{
public void Go(B b){
b.go();
}
}
interface B{
void go();
}
4. 成员内部类
-
示例: class Outer{
int a = 100;
public void Outf(){}
class Inner{
int a = 9;
public void Inf(){
Outf();
System.out.println(Outer.this.a);
}
}
public void Outf1(){
Inner inner = new Inner();
System.out.println(inner.a);
}
}
-
在外部类外使用该内部类 public class Test{
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner =outer.new Inner();
}
}
-
成员内部类可以直接可以直接访问外部类的所有成员。如果名字相同,遵循就近原则,此时访问外部类成员可以使用外部类名.this.成员 -
可以添加访问修饰符(public、默认、protected、private),相当于一个成员
5. 静态内部类
-
示例: class Outer{
static int a = 100;
static int b = 10;
public void Outf(){}
static class Inner{
static int b = 9;
void Inf(){
System.out.println(a);
System.out.println(b);
System.out.println(Outer.b);
Outer outer = new Outer();
outer.Outf();
}
}
public void Outf1(){
System.out.println(Inner.b);
Inner inner = new Inner();
inner.Inf();
}
}
-
在外部类外访问该静态内部类 public class Test{
public static void main(String[] args) {
System.out.println(Outer.Inner.b);
Outer.Inner inner = new Outer.Inner();
inner.Inf();
}
}
-
成员内部类可以直接可以直接访问外部类的静态成员。如果名字相同,遵循就近原则,此时访问外部类静态成员可以使用外部类名.成员 -
可以添加访问修饰符(public、默认、protected、private),相当于一个成员
七、枚举
1.简介
-
对于某一个变量只有几种固定取值时,常声明为枚举类型。如:季节。 -
语法:使用关键字enum声明枚举类型 [修饰符] enum 枚举类型名 {
枚举成员
方法
}
-
枚举成员也称枚举常量或枚举值,不能重名,用, 分隔,都默认被final public static修饰 -
这里枚举类型名,也是枚举成员的数据类型,所有枚举成员也称为枚举实例或枚举对象 -
枚举是一种特殊的类,会默认继承java.lang.Enum 类,因此不能再继承其他类,但可以实现接口。 -
非抽象的枚举会默认使用final 修饰,因此不能派生子类 -
枚举的所有枚举成员必须放在枚举体的第一条语句。
2.不包含方法的枚举
3. ?包含属性和方法的枚举
-
如果枚举成员,需要有更丰富的内容,这时就可以赋予他们属性和方法 -
示例:
enum Season{
SPRING("春"),SUMMER("夏"),AUTUMN("秋"),WINTET("冬");
private String name;
Season(String name) {
this.name = name;
}
public String toString() {
return name;
}
void play(){
System.out.println(name+"天就要去玩耍");
}
}
-
-
枚举的构造方法只能是private 修饰的,可以省略。 -
因此枚举成员必须在定义时就加上(参数列表) ,如果有无参构造器,则可省略()
🦀🦀观看。
待续~~
|