(JAVA)类与对象(进阶)

(JAVA)类与对象(进阶)

一、包

1. 包的简介

  1. 每个包对应一个文件夹,文件夹里还可以有子类文件夹,就是子包

  2. 当你需要用到包的概念时,需要在.java文件的第一条语句,声明该文件是在什么包下

package learn.java_.test;
public class Test {  
}

在这里插入图片描述

2. 包的使用

  1. 这时候可能有朋友疑问了:为什么不把文件路径写全呢?

    我的理解:你声明该文件的第一个包名为learn,那么在当你需要使用Test类时,只需要把文件夹learn直接复制到你使用的位置,就可以使用了。因为前面没写的部分,会默认在你使用的当前目录下去找learn\java_\test

  2. 示例:

    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包下所有的文件。

  1. 这里思考一下,既然上面示例中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类,因此会报错的。

  2. 在同一包中的类默认情况下可以互相访问

二、🐯继承

1. 简介

  • 一个子类只能有一个父类,而一个父类可以有多个子类。

  • 父类实际上是所有子类的公共成员的集合。

  • 被继承的类称为父类或者超类,由继承而得到的类称为子类

  • 使用关键字extends,示例:

    //A继承了B
    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) {
            //调用B中的public B(int b)
            super(b);
        }
    }
    
    class B {
        int b;
        public B(int b) {
            this.b = b;
        }
    }
    
  • 使用方法:super(参数列表)

5.🐻方法重写

  • 也称方法覆盖(override)

  • 在子类中如果有一个方法,其方法名返回类型参数都和父类中的某一方法一样,那么该子类方法就重写了父类同名方法的功能

  • 示例:

    public class A extends B{
        //重写了B的say方法
        void say() {
            System.out.println("A");
        }
    }
    class B {
        void say() {
            System.out.println("B");
        }
    }
    
  • 重写的方法的返回类型可以和父类方法一样,或者是父类返回类型的子类

  • 👿重写的方法的访问权限可以比父类的大,但是不能比父类的小。

    如:示例中第3行省略的访问修饰符可以改成public,但是不能是protected

  • 子类中不能重写父类中有finalstatic所修饰的方法。

对于成员变量没有重写的概念。

6. 对象的多态

  • 一个对象的编译类型运行类型可以不一致

  • 编译类型是在定义时变量名前的类型,不能改变

  • 运行类型是创建对象时new的类型,可以更改。

  • 如:

    public class A extends B{
        
        public static void main(String[] args) {
            B b = new A();
        }
    }
    class B {
    }
    

    对象b的编译类型是B,而运行类型是A

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

  • 使用:引用名 instanceof 类型

  • 作用:如果该引用的对象的运行类型是该类型或该类型的子类型,返回true,否则返回false
    在这里插入图片描述

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 属性
  • 如果用final来修饰成员变量,则说明该成员变量为常量。必须有初始化,且不能修改了
  • 对其初始化有三种方式:
    //方式1:在定义时初始化
    class A{
        final int a = 10;
    }
    //方式2:在构造器中初始化
    class B{
        final int b;
        B(int b) {
            this.b = b;
        }
    }
    //方式3:在代码块中初始化
    class C{
        final int c;
        {
            c = 10;
        }
    }
    
9.3 方法
  • 如果用final来修饰成员方法,则表明该方法不能被子类所重写,可提高安全性
9.4 类
  • 如果用final来修饰,则该类不能继承,既不能做为父类,也不能做为子类。

四、抽象类

1.简介

  • 使用修饰符abstract所修饰的类
  • 抽象类是专门作为父类而创建的,它的作用类似于__模板__
  • 目的是根据抽象类的格式来创建子类,再由其子类来创建对象。
  • 因此抽象类不能创建实例化对象(new)

2.💫抽象方法

  • 使用修饰符abstract所修饰的方法

  • 使用:[其他修饰符] abstract 返回类型 方法名(参数列表);

    抽象方法没有{}(方法体)

  • 示例:

    //抽象类B
    abstract class B {
        //抽象方法boy()
        public abstract void boy();
    }
    
  • 抽象类中可以没有抽象方法,但是有抽象方法的类必须声明为抽象类

  • 抽象类的子类必须实现父类中的所有抽象方法,或者子类也是一个抽象类

    示例:

    abstract class B {
        public abstract void boy();
    }
    class A extends B{
        
        //实现父类中的抽象类
        public void boy() {    
        }
    }
    
    //子类C也是一个抽象类
    abstract class C extends B{    
    }
    
  • 抽象方法不能使用private、final、static来修饰,因为该关键字修饰的方法都不能重写。

3.其他

  • abstract只能用来修饰类和方法

  • 普通类有的成员,抽象类都可以有,因为抽象类的本质还是类

    只是不能实例化对象

五、接口

1.简介

  • 使用关键字interface来定义接口
  • //接口的定义
    [public/] interface 接口名{
    }
    
  • 接口也不能创建实例化对象(new)

2.🎏接口的成员

  • 属性,实际上是静态常量

    interface A {
        //属性a,必须初始化
        int ONEA = 100;
    }
    

    对于接口的属性都是public static final修饰的,当省略修饰符,系统会默认。
    如:int a=100;实际上是public static final int a=100;

  • 抽象方法

    interface A {
        //抽象方法apple()
        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来实现接口

  • 普通类实现接口必须实现接口所有的抽象方法,而抽象类则可不必。

    //接口A
    interface A {
        void apple();
    }
    //类B实现接口A
    class B implements A{
        //实现抽象方法apple()
        public void apple() {
        }
    }
    //抽象类C实现接口A
    abstract class C implements A{
    }
    
  • 因为接口中的方法都是public,所有在实现抽象方法时,需要显示的使用public修饰符,实现的方法不能缩小接口中该方法的访问控制范围(和方法重写一样)。

3.1用接口实现类的多重继承
  • java不支持类的多重继承,但是可以利用接口间接的解决这个问题

  • 一个类可以实现多个接口,它们间用,分隔

    interface A{}
    interface B{}
    class C implements A,B{}
    
  • 接口中的常量、抽象方法和默认方法可以被实现该接口的类所继承。但不能继承接口中的静态方法。
    在这里插入图片描述

3.2名字冲突
  • 一般指一个类实现多个接口,不同接口中相同的默认方法引起的冲突

    interface A{
        //常量apple
        int apple = 0;
        //默认方法
        default void to(){}
        //抽象方法
        void play();
    }
    interface B{
        //常量apple
        int apple = 10;
        //默认方法
        default void to(){}
        //抽象方法
        void play();
    }
    class C implements A,B{
        //重写to() 
        //这里没有default
        public void to() {
            //
            A.super.to();
        }
    	//实现play()
        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 a = new B();
            a.apple();
        }
    }
    
    interface A {
        void apple();
    }
    class B implements A{
        //实现抽象方法apple()
        public void apple() {
        }
       	//B特有的boy()
        void boy(){}
    }
    
    
  • 也有相同的向下转型

    B b = (B)a;
    b.boy();
    

5. 接口的继承

  • 和类相似,接口也可以继承,使用extends来表明继承关系

  • 一个接口可以继承多个父接口,它们间用,分隔

    interface A{
    }
    interface B{
    }
    //接口C继承接口A和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();
                    //Outer.this本质就外部类的对象
                    System.out.println(Outer.this.a);//1
                    //就近原则
                    System.out.println(a);//2
                    
                }
            }
            //使用:
            Inner inner = new Inner();
            inner.Inf();
        }
        
    }
    
  • 在内部类里可以直接访问外部类的所有成员,对于成员重名问题,默认遵循就近原则,然后如果是访问外部类的成员,可以使用外部类名.this.成员来访问

  • 不能有访问修饰符(public、private和protected)。因为它相当于一个局部变量

  • 作用域:在定义它的方法或代码块的{}范围中

  • 使用:在作用域的{}范围内,先创建对象,再访问。

3.🔑匿名内部类

  • 如果某个类的对象只用一次,则可在定义类的同时就创建该类的对象。

  • 这种定义类的方法不取名字,所以称为匿名内部类,类名由系统分配。

  • 语法:返回的是一个对象的引用

    new/接口(参数列表) {
        类体
    };
    
  • 其中类/接口,是匿名内部类所继承的类实现的接口,类也可以是抽象类,但都只能用一个。

  • 示例:

    class Outer{//外部类
    	int a = -1;
        public void f(){
            //定义匿名内部类
            //(100)会调用父类的构造器
            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();
            //直接传递一个实现接口B的匿名内部类
            a.Go(new B(){
                //实现抽象方法go()
                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.不包含方法的枚举

  • 因为自动继承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. ✨包含属性和方法的枚举

  • 如果枚举成员,需要有更丰富的内容,这时就可以赋予他们属性和方法

  • 示例:

    //定义枚举类型Season
    enum Season{
        //声明4个枚举成员
        SPRING("春"),SUMMER("夏"),AUTUMN("秋"),WINTET("冬");
        //属性
        private String name;
    	//构造器,默认是private
        Season(String name) {
            this.name = name;
        }
        //重写toString()
        public String toString() {
            return name;
        }
        void play(){
            System.out.println(name+"天就要去玩耍");
        }
    }
    
  • 在这里插入图片描述

  • 枚举的构造方法只能是private修饰的,可以省略。

  • 因此枚举成员必须在定义时就加上(参数列表),如果有无参构造器,则可省略()

🦀🦀观看。

待续~~

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值