深入类和对象

Posted Al_tair

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入类和对象相关的知识,希望对你有一定的参考价值。

深入类和对象

大家好呀!我是小笙!我学习了韩顺平老师的类和对象的知识,收获颇丰!现在来和大家分享笔记!

深入类和对象

类变量和类方法

类变量也称作静态变量,类方法也称作静态方法

与成员变量的区别就是有无 static 关键字

注意细节

  1. 静态变量是同一个类所有对象共享
  2. 类变量在类加载的时候就生成了
  3. 类变量中不能使用和对象有关的关键字(比如:,this,super ),因为this或者super的产生需要创建对象,但是类变量在类加载的时候就出现了
  4. 类方法只能访问类变量或者类方法,但是反之普通成员方法既可以访问非静态成员,也可以访问静态成员
  5. 类变量的生命周期随着类的加载开始,随着类消亡而销毁
  6. 如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。换句话说,父类和子类中含有的其实是两个没有关系的方法,它们的行为也并不具有多态性

语法:访问修饰符 static 数据类型 变量名; (访问修饰符和static的顺序可以交换)

访问方式: 类名.类变量(推荐) 对象名.类变量名

// 举例类变量和类方法的使用
// Math类的源码
 public static final double PI = 3.14159265358979323846;
// 取绝对值
@HotSpotIntrinsicCandidate
public static int abs(int a) 
    return (a < 0) ? -a : a;

Main方法

解释main方法的形式; pubLic static void main(String[]args)

public class HelloMain 
    /*
     * 1.main方法时虚拟机调用
     * 2.java虚拟机调用类的main方法,所以该方法的访问权限必须是public
     * 3.java虚拟机在执行main()方法时不必创建对象,所以用static
     * 4.Java执行程序 参数1 参数2 参数3【举例说明: java HelloMain hello main !】 参数1:hello 参数2:main 参数3:!
     *
     * 特别提示:
     * 1.在main()方法中,我们可以直接调用main方法所在的类的静态方法和静态属性
     * 2.但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象访问非静态成员
     */
    public static void main(String[]args)
        // args 是如何传入 在idea上设置 Run ->  Edit Configurations  ->  program arguments:
        // 遍历显示
        for(int i=0;i<args.length;i++)
            System.out.println("第"+(i+1)+"个参数"+args[i]);
        
    

代码块

普通代码块

普通代码块又称初始化块,属于类中的成员部分,没有方法名,没有返回,没有形参,只有方法体

普通代码块不需要被调用,而是加载对象的时候隐式调用

语法: // 方法体 ; 没有修饰符 ;分号可有可无

public class CodeBlock 
    /*
     * 代码块
     * 1.相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
     * 2.场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
     */
    public static void main(String[]args)
        new Movie("爱情神话");
    

class Movie
    private String name;
    private double price;
    private String director;

    /*
     * 下面的三个构造函数都有相同的语句,这样看起来重复
     * 代码块的使用就可以很好的解决这个问题
     */
    
        System.out.println("电影开始~~");
    ;
    public  Movie()
    public Movie(String name)
//        System.out.println("电影开始~~");
        this.name = name;
    
    public Movie(String name,double price)
//        System.out.println("电影开始~~");
        this.name = name;
        this.price = price;
    
    public Movie(String name,double price,String director)
//        System.out.println("电影开始~~");
        this.name = name;
        this.price = price;
        this.director = director;
    

注意细节

  • 普通代码块是随着对象的创建而执行的,有几个对象就运行几次代码块
  • 如果不创建对象,只是使用类的静态成员,不会执行代码块
静态代码块

静态代码块,属于类中的成员部分,没有方法名,没有返回,没有形参,只有方法体和修饰符static

静态代码块不需要被调用,而是加载类的时候隐式调用

语法:static // 方法体 ; ;分号可有可无

类什么时候被加载?【重点】

  1. 创建对象化实例时,并且父类的也会被加载
  2. 使用类的静态成员时(静态属性,静态方法)
  3. 使用子类的静态成员时

静态代码块的例子

public class StaticCodeBlock 
    /*
     * 父类和子类的静态代码块和普通代码块执行顺序是什么呢?
     * 在父类和子类都有静态代码块和普通代码块时候,创建子类实例时候的执行顺序: 父类静态代码块 --> 子类静态代码块 --> 父类普通代码块 --> 子类普通代码块
     * 在父类和子类都有静态代码块和普通代码块时候,创建父类实例时候的执行顺序: 父类静态代码块 --> 父类普通代码块
     * 在父类和子类都有静态代码块和普通代码块时候,使用父类的静态成员时只调用父类的静态代码块
     * 在父类和子类都有静态代码块和普通代码块时候,使用子类的静态成员时候的执行顺+序: 父类静态代码块 --> 父类普通代码块
     *
     * 静态属性初始化,静态代码块,静态方法,构造器的优先级
     * 1.父类的静态属性初始化优先级 == 静态代码块优先级 (看代码的执行顺序)
     * 2.子类的静态属性初始化优先级 == 静态代码块优先级 (看代码的执行顺序)
     * 3.父类的成员变量初始化优先级 == 普通代码块优先级 (看代码的执行顺序)
     * 4.父类的构造器
     * 5.子类的成员变量初始化优先级 == 普通代码块优先级 (看代码的执行顺序)
     * 6.子类的构造器
     * 方法都需要调用才会执行
     * public 构造函数名()
     *    1)super()  
     *    2)普通代码块
     *    3)构造函数内容
     * 
     */
    public static void main(String[]args)
        // 使用类的静态成员时(静态属性,静态方法)
        // AA.name = "念笙";
        // AA.show();

        // 创建对象实例时(new)
        // new AA();

        // 创建子类的对象实例,父类也会被加载
        // new BB();

        // 使用子类的静态成员时(静态属性,静态方法)
        // BB.sex = "男";
        // BB.show();

        // 静态初始化,静态代码块,静态方法的优先级
           new CC();
        //   CC.show();
    


class AA
    public static String name;

    static 
        System.out.println("AA静态代码块被调用");
    
    
        System.out.println("AA普通代码块被调用");
    
    public static void show()
        System.out.println("name:"+name);
    


class BB extends AA
    public static String sex;
    static 
        System.out.println("BB静态代码块被调用");
    
    
        System.out.println("BB普通代码块被调用");
    
    public static void show()
        System.out.println("sex:"+sex);
    


class CC
    public CC()
        // 1)super()
        // 2)普通代码块
        System.out.println("构造器被调用");
    
    // 静态属性初始化
    private static int age = getAge();

    // 静态方法
    public static void show()
        System.out.println("age:"+age);
    

    // 静态代码块
    static
        System.out.println("CC静态代码块被调用");
    

    public static int getAge()
        System.out.println("age:"+age);
        return 18;
    

final关键字

final 关键字可以用来修饰类,属性,方法和局部变量 不能修饰构造器

public class Final 
    /*
     * 使用final的情况
     * (1)当不希望类被继承时,可以用final修饰
     * (2)当不希望父类的某个方法被子类覆盖或者重写
     * (3)当不希望类的某个属性的值被修改,可以用final修饰 [例如:final double PI = 3.1415926] -> 常量
     * (4)当不希望某个局部变量被修改,可以用final修饰 -> 局部常量
     *
     * 使用final关键字的细节讨论
     * 1.final修饰的属性又叫常量,一般用 XXX_XXX_XXX来命名
     * 2.final修饰的属性在定义时,必须赋初始值,赋值可以加在如下的位置上
     *    属性定义时初始化 / 在构造器中 / 在代码块中
     *   final修饰的属性是静态的,则,赋值可以加在如下的位置上
     *    静态属性定义时初始化 / 在静态代码块中
     * 3.final类不能继承,但是可以实例化对象
     * 4.如果类不是final类,但是含有final方法,虽然该方法不能重写,但是该类可以被继承
     * 5.final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化 如下例子
     * 6.包装类(Integer,double,Float,Boolean等)不能继承
     */
    public static void main(String[] args) 
        /*
         * final 修饰基本数据类型
         * 基本数据类型的值不能发生变化
         */
        final int age = 30;
        // sge = 100; 报错

        /*
         * final修饰引用数据类型
         * 引用数据类型饿地址值不能发生改变,但是该地址上的内容可以发生改变
         */
        final FU fu = new FU();
        // fu.show4();
        // fu = new FU();  报错
        
        // final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化
        System.out.println(BB.name);
    


class AA
   // 静态属性定义时初始化
    public static final int NUMS1 = 12;
    public final int NUM1 = 12;

    public static final int NUMS2;
    public final int NUM2;

    public static final int NUMS3;
    public final int NUM3;
    
    // 在静态代码块中初始化
    static
        NUMS2 = 12;
        NUMS3 = 12;
    
    
        NUM3 = 12;
    
    // 在构造器中初始化
    public AA()
        NUM2 = 12;
    


// final 和 static 往往搭配使用效率更高,不会导致类加载,底层编译器做了优化
class BB
    public static final String name = "yyds";
    static 
        System.out.println("类加载了");
    
    // 运行效果:不会显示类加载了 说明类没有被加载


抽象类

引出:当父类的某些方法需要申明,但又不确定如何实现的时候,可以把这个类变成抽象类 添加 abstract关键字

抽象类的本质还是类可以有方法,属性,代码块,构造器,但是抽象类不能被实例化

语法:

  • 类:访问修饰符 abstract class 类名
  • 方法:访问修饰符 abstract 返回类型 方法名(参数列表); // 没有方法体
public class AbstractDemo 
    /*
     * 抽象类 ==> 简化父类方法的不确定代码
     * 抽象类会被继承,由自己的子类实现方法
     *
     * 注意事项: 
     * 1.抽象类不能被实例化,也不能直接通过类名.静态属性的方法来访问抽象类中的静态属性,可以被子类用super关键字调用构造器
     * 2.抽象方法对应抽象类或者接口;但是有抽象类,不仅可以没有抽象方法还可以有方法体的普通方法
     * 3.abstract 只能修饰类和方法
     * 4.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract,但是如果子类实现抽象类的全部抽象方法,则子类的子类就不需要      *   实现了抽象方法
     * 5.抽象方法不能使用private,final和static来修饰,因为这些关键字都是和重写相违背的
     */
    public static void main(String[] args) 
        // 抽象类不能被实例化
        // Animal animal = new Animal(); 报错
    


// 当一个类中存在抽象方法时候,需要将该类声明成为abstract类
abstract class  Animal
    public String name;
    // 思考:有意义吗??  ==> 方法不确定性 ==> 考虑设计成抽象类方法(abstract)即是没有方法体 ==> 让子类继承实现
    //    public void eat()
    //        System.out.println("吃撒");
    //    
    public abstract void eat();

接口

概念:接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。

语法:访问修饰符 interface 接口名 访问修饰符:public 和默认 ; class 类名 implements 接口 // 必须实现的抽象方法

注意细节

  • 在jdk7之前都是没有方法体的抽象方法,在jdk8以后接口的方法可以有具体实现,但是必须是静态方法或者是默认方法

    // 接口中的所有方法都是public修饰的方法,接口中的抽象方法可以不用被abstract修饰
    // 可以通过接口名.静态方法名来调用静态方法 接口名.属性名来访问属性
    interface Interface
        // 属性 默认是public  static final修饰
        int n1 = 10;
    
        // 抽象方法  默认是public abstract
         void method1();
    
        // 默认方法 添加default关键字  默认是public 
        default public void method2()
            System.out.println("默认方法");
        
    
        // 静态方法
        public static void  method3()
            System.out.println("静态方法");
        
     
    
  • 接口不能被实例化

  • 一个非抽象类实现了该接口就必须实现该接口上的所有抽象方法,抽象类去实现接口时候,可以不用实现接口的抽象方法

  • 一个类同时可以实现多个接口,一个接口不能继承其他的类,但是可以继承多个其他接口

    // 顶级接口
    interface top
    
    // 父级接口  一个接口不能继承其他的类,但是可以继承多个其他接口
    interface secondTop extends top
    
    //另外一个接口
    interface another
    
    // 实现类 一个类同时可以实现多个接口
    class imp implements secondTop,another
    

实现接口 Vs 继承类

  1. 接口和继承解决的问题不同
    继承的价值主要在于:解决代码的复用性和可维护性
    接口的价值主要在于:设计好各种规范(方法),让其他类去实现这些方法

  2. 接口比继承更加灵活
    继承满足is-a的关系,而接口只需满足like-a的关系

  3. 接口在一定程度上实现代码解耦[接口的规范性+动态绑定]

抽象类和接口的区别

  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
  • 抽象可以有普通方法(含有方法体),接口只能是默认方法(带有default关键字)

接口的多态性

// Usb接口
public interface UsbInterface   
    // 规定相关接口的方法
    public void start();
    public void end();


// 相机实现了该接口
public class Camera implements UsbInterface 
    @Override
    public void start() 
        System.out.println("照相机开始运行了~");
    

    @Override
    public void end() 
        System.out.println("照相机开始关机了~");
    


// 手机实现了该接口
public class Phone implements UsbInterface // 实现接口
    @Override
    public void start() 
        System.out.println("手机开始运行了~");
    

    @Override
    public void end() 
        System.out.println("手机开始关机了~");
    


// 接口插入,分别实现不同功能
public class Computer 
    // 计算机工作
    public  void work(UsbInterface usbInterface)
        usbInterface.start();
        usbInterface.end();
    


public class Interface01 
    /*
     * 接口多态特性
     * 1)既可以接收手机对象,又可以接受相机对象,就体现了接口多态(接口引用可以指向实现了该接口的类的对象)
     * 2)多态数组
     * usbs[i] instanceof Phone 向下转型,判读Usb接口是否为Phone
     * 接口类型的变量可以指向实现了该接口的类的对象的实例
     */
    public static void main(String[]args)
        // 创建手机和相机对象
        Camera camera = new Camera();
        Phone phone = new Phone();
        // 创建电脑对象
        Computer computer = new Computer();
        // 插入接口,相机和手机分别运作
        computer.work(phone);
        computer.work(camera);
        // 多态数组
        UsbInterface[] UsbS = new UsbInterface[2]; 
        UsbS[0] = new Phone(); 
        UsbS[1] =  new Camera();
    


内部类

概念:一个类的内部嵌套了另一个类结构,被嵌套的类称为内部类,嵌套其他类的类称为外部类(注意类的五大成员:属性,方法,构造器,代码块,内部类)

class OuterClass // 外部类
    int n1 = 100; // 属性
    public OuterClass(int n3) // 构造方法
    void n2() //方法
     // 代码块
    class InnerClass // 内部类

语法:class Outer // 外部类 class Inner // 内部类

内部类的分类:

  • 定义在外部类局部位置(比如方法内)
    • 局部内部类(有类名)
    • 匿名内部类(没有类名,重点)
  • 定义在外部类的成员位置上
    • 成员内部类(没有static修饰)
    • 静态内部类(有static修饰)
局部内部类

局部内部类是定义在外部类的局部位置(比如:方法中),并且有类名

作用域:仅在定义它的方法或代码块中

class OuterClass02 // 外部类
    private int n1 = 100;
    public void n2()
        /*
         * 局部内部类
         * 1.局部内部类是定义在外部类的局部位置,通常在方法里或者代码块里
         * 2.局部内部类可以访问外部类的所有成员变量,包含私有的
         * 3.局部内部类不能添加访问修饰符,但是是可以被final修饰,可以不被继承
         * 4.作用域:仅在定义它的方法或代码块中
         * 5.外部类在方法中可以创建InnerPartClass对象,然后调用局部内部类的方法,不允许在方法外创建该对象
         * 6.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果就想访问外部类的成员,则可以使用(外部类名.this.成员)
         */
        class InnerPartClass 
            private int n1 = 20;
            public void n3()
                // 7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
                System.out.println(n1); // 20
                // 解读OuterClass02.this 本质就是外部类的对象即那个对象调用了n2方法,OuterClass02.this就是那个对象,这里是out对象调用的,因此					  OuterClass02.this指向out对象
                System.out.println(OuterClass02.this.n1); // 100
                System.out.println(OuterClass02.this); // com.Al_tair.innerClass_.OuterClass02@27d6c5e0
            
        
        // 5.外部类在方法中可以创建InnerPartClass对象,然后调用局部内部类的方法
        InnerPartClass innerPartClass = new InnerPartClass();
        innerPartClass.n3();
    


public class InnerClass01  // 外部其他类
    public static void main(String[] args) 
        OuterClass02 out = new OuterClass02();
        out.n2();
        System.out.println(out); // com.Al_tair.innerClass_.OuterClass02@27d6c5e0
    

匿名内部类

概念:定义在外部类的局部位置,没有类名

作用域:仅仅在定义它的方法和代码块中

语法:new 类名或接口名(参数列表) // 匿名内部类

public class AnonymousInnerClass 
    /*
     * 匿名内部类
     * 定义在外部类的局部位置<=>局部内部类(没有表露出来的类名)
     * 基本语法:new 类或接口(参数列表)
     */
    public static void main(String[] args) 
        OutClass outClass = new OutClass();
        outClass.meathod();
    


class OutClass
    private int n = 10;
    public void meathod()
        /*
         * 使用匿名内部类简化开发
         * aa的编译类型 -- AA ; aa的运行类型 -- 匿名内部类OutClass$1 (外部类名$匿名内部类的序号)
         * 1.创建匿名内部类后马上创建该实例,并返回该地址给aa
         * 2.匿名内部类只能使用一次,但是该实例对象可以反复引用,就是不能用一个匿名内部类创建多个对象实例
         * 3.不能添加访问修饰符,因为它的就是一个局部变量
         * 4.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)
         */
        // 第一种调用匿名内部类的内部方法
        // 这里new的不是接口实例化,而是接口的匿名内部实现类
        AA aa = new AA() // 向上转型
            @Override
            public void cry() 
                System.out.println("嘤嘤嘤~~");
            
        ;
        aa以上是关于深入类和对象的主要内容,如果未能解决你的问题,请参考以下文章

使用Java语言深入理解程序逻辑——类和对象

使用Java语言深入理解程序逻辑——类和对象

使用Java语言深入理解程序逻辑——类和对象

java 面向对象内部类和接口

java之内部类精选

深入理解C++类和对象(下)