4面向对象高级

类变量和类方法

类变量

    1.类变量(静态变量static修饰)是类所有对象共享的,jdk8以前是存放在方法区,jdk8及以后是存放在堆区

    2.类变量是在类加载时就初始化了(即使没有创建对象,只要类加载,就可以使用类变量)。

    3.类变量是该类的所有对象共享的,实例变量是每个对象独享的

基本语法:访问修饰符 static 数据类型 变量名

访问类变量(类方法一样):1.类名.类变量名(推荐使用)

​                                              2.对象名.类变量名(静态变量的访问修饰符的访问权限和范围和普通属性是一样的)

public class ChildGame {
    public static void main(String[] args) {
        Child child1 = new Child("白骨精");
        child1.join();
        child1.count++;

        Child child2 = new Child("狐狸精");
        child2.join();
        child2.count++;

        Child child3 = new Child("老鼠精");
        child3.join();
        child3.count++;

        //===========
        //类变量,可以通过类名来访问
        System.out.println("共有" + Child.count  + " 小孩加入了游戏...");
        //下面的代码输出什么?
        System.out.println("child1.count=" + child1.count);//3
        System.out.println("child2.count=" + child2.count);//3
        System.out.println("child3.count=" + child3.count);//3
    }
}

class Child { //类
    private String name;
    //定义一个变量 count ,是一个类变量(静态变量) static 静态
    //该变量最大的特点就是会被Child 类的所有的对象实例共享
    public static int count = 0;
    public Child(String name) {
        this.name = name;
    }
    public void join() {
        System.out.println(name + " 加入了游戏..");
    }
}

public class VisitStatic {
    public static void main(String[] args) {

        //类名.类变量名
        //说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
        System.out.println(A.name);
        A a = new A();
        //通过对象名.类变量名
        System.out.println("a.name=" + a.name);

    }
}

class A {
    //类变量
    //类变量的访问,必须遵守 相关的访问权限.
    public static  String name = "韩顺平教育";
    //普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量(不同的叫法)
    private int num = 10;

}

类方法

类方法也叫静态方法

基本语法:访问修饰符 static 返回类型 方法名(参数列表){}

类方法不能使用和对象有关的关键字(this和super),普通方法可以使用

类方法只能访问静态变量和静态方法,普通成员方法可以普通变量(方法)和静态变量(方法)

理解main方法语法static

public static void main(String【】args){}

    1.main方法是java虚拟机调用,所以访问权限是public

    2.java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static

    3.该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数

代码块

代码块又称为初始化块,属于类中的成员,类似于方法,通过{}包围起来,只有方法体(修饰符只有static或无(静态代码块,普通代码块)),加载类或者创建对象时隐式的调用,(对构造器的补充,做初始化操作

代码块的使用细节

1.static 代码块也叫作静态代码块:

对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。 \textcolor{blue}{对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。} 对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。

2.什么时候类加载?

     //1. 创建对象实例时(new)
       AA aa = new AA();
     //2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
       AA aa2 = new AA();
     //3. 使用类的静态成员时(静态属性,静态方法)
       System.out.println(Cat.n1); 
class AA extends BB {}

3.创建一个对象时,在一个类的调用顺序:

    1.调用静态代码块和静态属性初始化(定义顺序)

    2.调用普通代码块和普通属性的初始化(定义顺序)

    3.构造方法

public class CodeBlockDetail03 {
    public static void main(String[] args) {
        A a = new A();// (1) A 静态代码块01 (2) getN1被调用...(3)A 普通代码块01(4)getN2被调用...(5)A() 构造器被调用
    }
}

class A {
    { //普通代码块
        System.out.println("A 普通代码块01");
    }
    private int n2 = getN2();//普通属性的初始化


    static { //静态代码块
        System.out.println("A 静态代码块01");
    }

    //静态属性的初始化
    private static  int n1 = getN1();

    public static int getN1() {
        System.out.println("getN1被调用...");
        return 100;
    }
    public int getN2() { //普通方法/非静态方法
        System.out.println("getN2被调用...");
        return 200;
    }

    //无参构造器
    public A() {
        System.out.println("A() 构造器被调用");
    }

}

4.构造器的最前面其实隐含了super()和调用普通代码块(以及普通属性初始化)。静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块

class A{
	public A(){
  	//1.super()  2.调用普通代码块
   	 System.out.println("构造器被调用")
	}
}
public class CodeBlockDetail04 {
    public static void main(String[] args) {
        new BBB();//(1)AAA的普通代码块(2)AAA() 构造器被调用(3)BBB的普通代码块(4)BBB() 构造器被调用
    }
}

class AAA { //父类Object
    {
        System.out.println("AAA的普通代码块");
    }
    public AAA() {
        //(1)super()
        //(2)调用本类的普通代码块
        System.out.println("AAA() 构造器被调用....");
    }
}

class BBB extends AAA  {
    {
        System.out.println("BBB的普通代码块...");
    }
    public BBB() {
        //(1)super()
        //(2)调用本类的普通代码块
        System.out.println("BBB() 构造器被调用....");
    }
}

创建一个子类对象时(继承关系),调用顺序

    1.父类静态代码块和静态属性(定义顺序)

    2.子类静态代码块和静态属性(定义顺序)

    3.父类的普通代码块和普通属性初始化(定义顺序)

    4.父类的构造方法

    5.子类的普通代码块和普通属性初始化(定义顺序)

    6.子类的构造方法

单例设计模式

某个类只能存在一个对象实例,并且该类只能提供一个取的其对象实例的方法

步骤:1.构造器私有化(防止用户直接new)
           2.类内部创建对象
           3.向外暴露一个静态的公共方法

饿汉式


public class SingleTon01 {

    public static void main(String[] args) {
//        GirlFriend xh = new GirlFriend("小红");
//        GirlFriend xb = new GirlFriend("小白");

        //通过方法可以获取对象
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);

        GirlFriend instance2 = GirlFriend.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);//T
        //System.out.println(GirlFriend.n1);
    }

}

//有一个类, GirlFriend
//只能有一个女朋友
class GirlFriend {

    private String name;
    //public static  int n1 = 100;
    //为了能够在静态方法中,返回 gf对象,需要将其修饰为static
    //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.
    private static GirlFriend gf = new GirlFriend("小红红");

    //如何保障我们只能创建一个 GirlFriend 对象
    //步骤[单例模式-饿汉式]
    //1. 将构造器私有化
    //2. 在类的内部直接创建对象(该对象是static)
    //3. 提供一个公共的static方法,返回 gf对象
    private GirlFriend(String name) {
        System.out.println("構造器被調用.");
        this.name = name;
    }

    public static GirlFriend getInstance() {
        return gf;

    }
}

懒汉式


public class SingleTon02 {
    public static void main(String[] args) {

        Cat instance = Cat.getInstance();
        System.out.println(instance);

        Cat instance2 = Cat.getInstance();
        System.out.println(instance2);

        System.out.println(instance == instance2);//T

    }
}
class Cat {
    private String name;
    public static  int n1 = 999;
    private static Cat cat ; //默認是null
    //4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象
    private Cat(String name) {
        System.out.println("構造器調用...");
        this.name = name;
    }
    public static Cat getInstance() {
        if(cat == null) {//如果還沒有創建cat對象
            cat = new Cat("小可愛");
        }
        return cat;
    }

}

final关键字

final可以修饰 类,方法,属性,局部变量

    1.修饰类:该类无法被继承

    2.修饰方法:无法被子类重写

    3.修饰属性和局部变量:该值无法被修改

使用细节:

    1.final修饰的属性又叫做常量,一般命名XX-XX

    2.final修饰的属性必须赋初值,并且以后无法被修改,(1.定义时 2.构造器中 3.代码块中)

     3.如果final修饰的属性是静态的,则初始化的位置只能在定义时代码块中

    4.final类不能被继承,但是可以实例化对象

    5.final方法不能被重写,但是可以继承(前提该方法不是在final类下)

    6.如果一个类是final类,就没必要将方法设置成final方法

    7.final不能修饰构造器

    8.final和static搭配使用,效率更高,修饰基本数据类型和String在调用时不会导致类加载

    9.包装类(Integer,Double等都是final类)String类也是final类

抽象类

当父类的某些方法,需要声明,但是又不确定如何实现,可以将其声明为抽象方法,那这个类就是抽象类

public class Abstract01 {
    public static void main(String[] args) {
       //Animal an=new Animal() 错误 抽象类不能被实例化
       //一个类继承了抽象类,则必须实现抽象类的所有抽象方法,除非自己声明为abstract     
    }
}

abstract class Animal { //抽象类不一定包含abstract方法,
    					//抽象类可以有任意成员,非抽象方法,构造器,静态属性
    //抽象类的构造器不能被直接调用,因为抽象类本身不能被实例化。只有通过子类来创建对象时,才会调用抽象类的构造器。
    			        //一旦包含了abstract方法,该类必须声明为abstract
    					//abstract只能修饰类和方法,不能修饰属性和其他
    private String name;
    //思考:这里eat 这里你实现了,其实没有什么意义
    //即: 父类方法不确定性的问题
    //===> 考虑将该方法设计为抽象(abstract)方法
    //===> 所谓抽象方法就是没有实现的方法
    //===> 所谓没有实现就是指,没有方法体
    //===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
    //===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.
//    public void eat() {
//        System.out.println("这是一个动物,但是不知道吃什么..");
//    }
    public abstract void eat()  ;//抽象方法不能使用private,final和static修饰,因为和重写相违背 
}

static与重写违背

static修饰的父类方法,子类也可以取相同的方法名、相同的修饰符、相同的返回值类型,但实际上已经不是同一个方法了

class Father{
    public static void father(){
        System.out.println("father");
    }
}

class Son extends Father{
    public static void father(){
        System.out.println("son");
    }
}

class Test{
    public static void main(String[] args){
        Father son=new Son();
        son.father();//输出的结果为father
    }
}

接口

接口就是给出一些没有实现的方法,封装到一起。

基本语法:interface 接口名{              class 类名 implements 接口{
                        //方法                                 自己的方法;
​                    }                                             必须实现的接口方法;}

在jdk8以前,接口的所有方法都没有方法体,即都是抽象方法,jdk8及以后有静态方法和默认方法,可以有方法的具体实现

public interface AInterface {
	//6.接口中的属性,只能是final的,而且是 public static final 修饰符
    public int n1 = 10; //等价 public static final int n1 = 10;

    
    //在接口中,抽象方法,可以省略abstract关键字
    public void hi();
    
    //在jdk8后,可以有默认实现方法,需要使用default关键字修饰
    public default void ok() { //显式的default关键字和默认(default)访问修饰符不是一回事
        System.out.println("ok ...");
    }
    //在jdk8后, 可以有静态方法
    public static void cry() {
        System.out.println("cry ....");
    }
}
public class InterfaceDetail01 {
    public static void main(String[] args) {
        //new IA();错误//1.接口不能被实例化
    }
}
//2.接口的修饰符和类一样,public和默认
interface IA {
    void say(){};//3.接口中所有的方法是 public方法,  接口中抽象方法,可以不用abstract 修饰
}
interface IB {//interface IB extends IA{  4.接口不能继承其他类,但是可以继承其他接口
    void hi(){};
}
class Cat implements IA,IB{ 
    //5.一个类同时可以实现多个接口
    //6.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决
    @Override
    public void say() {
    } 
    @Override
    public void hi() {
    } 
}
abstract class Tiger implements  IA {}//7.抽象类去实现接口时,可以不实现接口的抽象方法
public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig = new Teacher();
        //如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口
        //那么,实际上就相当于 Teacher 类也实现了 IH接口.
        //这就是所谓的 接口多态传递现象.
        IH ih = new Teacher();
    }
}

interface IH {
    void hi();
}
interface IG extends IH{ }
class Teacher implements IG {
    @Override
    public void hi() {
    }
}

区别抽象类:

接口抽象类
属性修饰符public static final (可以省略)任意
构造方法
普通方法需要default或者static关键字修饰 (访问修饰符public可不写)任意
抽象方法public abstract(可以省略)不能省略abstract

内部类

一个类的内部又完整的嵌套了另一个类结构,被嵌套的称为内部类,嵌套其他类的类称为外部类。内部类可以直接访问私有属性,并且可以体现类的包含关系。

image-20240319104615072

class Outer { //外部类
    private int n1 = 100;//属性
    public Outer(int n1) {//构造器
        this.n1 = n1;
    }
    public void m1() {//方法
        System.out.println("m1()");
    }
    {//代码块
        System.out.println("代码块...");
    }
    class Inner { //内部类, 在Outer类的内部
    }
}

局部内部类

class Outer02 {//外部类
    private int n1 = 100;
    private void m2() {
        System.out.println("Outer02 m2()");
    }//私有方法
    public void m1() {//方法
        //1.局部内部类是定义在外部类的局部位置,通常在方法
        //2.可以直接访问外部类的所有成员,包含私有的
        //3.不能添加访问修饰符,但是可以使用final 修饰
        //4.作用域 : 仅仅在定义它的方法或代码块中
        final class Inner02 {//局部内部类(本质仍然是一个类)     
            private int n1 = 800;
            public void f1() {
                //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
                //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
                //   使用 外部类名.this.成员)去访问
                //   ssOuter02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象
                System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
                System.out.println("Outer02.this hashcode=" + Outer02.this);
                m2();
            }
        }
        //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }

}

匿名内部类

语法:new 类或接口(参数列表){
                 //类体
            };

public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04 { //外部类
    private int n1 = 10;//属性
    public void method() {//方法
        //1.需求: 想使用IA接口,并创建对象
        //2.传统方式,是写一个类,实现该接口,并创建对象
        //3. Tiger/Dog 类只是使用一次,后面再不使用
        //4. 可以使用匿名内部类来简化开发
        //5. tiger的编译类型 ? IA
        //6. tiger的运行类型 ? 就是匿名内部类  Outer04$1
        /*
            我们看底层 会分配 类名 Outer04$1
            class Outer04$1 implements IA {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            }
         */
        //7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址
        //   返回给 tiger
        //8. 匿名内部类使用一次,就不能再使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();
        tiger.cry();
        tiger.cry();

//        IA tiger = new Tiger();
//        tiger.cry();

        //演示基于类的匿名内部类
        //分析
        //1. father编译类型 Father
        //2. father运行类型 Outer04$2
        //3. 底层会创建匿名内部类
        /*
            class Outer04$2 extends Father{
                @Override
                public void test() {
                    System.out.println("匿名内部类重写了test方法");
                }
            }
         */
        //4. 同时也直接返回了 匿名内部类 Outer04$2的对象
        //5. 注意("jack") 参数列表会传递给 构造器
        Father father = new Father("jack"){

            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
        father.test();

        //基于抽象类的匿名内部类
        Animal animal = new Animal(){
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}

interface IA {//接口
    public void cry();
}
//class Tiger implements IA {
//
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
class Father {//类
    public Father(String name) {//构造器
        System.out.println("接收到name=" + name);
    }
    public void test() {//方法
    }
}

abstract class Animal { //抽象类
    abstract void eat();
}

匿名内部类使用细节

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {

        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类---不能访问----->匿名内部类
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;
    public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域 : 仅仅在定义它的方法或代码块中
        Person p = new Person(){
            private int n1 = 88;
            @Override
            public void hi() {
                //可以直接访问外部类的所有成员,包含私有的
                //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
                        " 外部内的n1=" + Outer05.this.n1 );
                //Outer05.this 就是调用 f1的 对象
                System.out.println("Outer05.this hashcode=" + Outer05.this);
            }
        };
        p.hi();//动态绑定, 运行类型是 Outer05$1

        //也可以直接调用, 匿名内部类本身也是返回对象
        // class 匿名内部类 extends Person {}
//        new Person(){
//            @Override
//            public void hi() {
//                System.out.println("匿名内部类重写了 hi方法,哈哈...");
//            }
//            @Override
//            public void ok(String str) {
//                super.ok(str);
//            }
//        }.ok("jack");


    }
}

class Person {//类
    public void hi() {
        System.out.println("Person hi()");
    }
    public void ok(String str) {
        System.out.println("Person ok() " + str);
    }
}
//抽象类/接口...

匿名内部类用途

public class InnerClassExercise01 {
    public static void main(String[] args) {

        //当做实参直接传递,简洁高效
        f1(new IL() {
            @Override
            public void show() {
                System.out.println("这是一副名画~~...");
            }
        });
        //传统方法
        f1(new Picture());

    }

    //静态方法,形参是接口类型
    public static void f1(IL il) {
        il.show();
    }
}
//接口
interface IL {
    void show();
}
//类->实现IL => 编程领域 (硬编码)
class Picture implements IL {

    @Override
    public void show() {
        System.out.println("这是一副名画XX...");
    }
}

成员内部类

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        //外部其他类,使用成员内部类的三种方式
        // 第一种方式
        // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员
        // 这就是一个语法,不要特别的纠结.
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
        // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();


    }
}

class Outer08 { //外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    //1.注意: 成员内部类,是定义在外部内的成员位置上
    //2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    public class Inner08 {//成员内部类
        private double sal = 99.8;
        private int n1 = 66;
        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
            //,可以通过  外部类名.this.属性 来访问外部类的成员
            System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + 							Outer08.this.n1);
            hi();
        }
    }
    //方法,返回一个Inner08实例
    public Inner08 getInner08Instance(){
        return new Inner08();
    }


    //写方法
    public void t1() {
        //使用成员内部类
        //创建成员内部类的对象,然后使用相关的方法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }
}

静态内部类

public class StaticInnerClass01 {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();

        //外部其他类 使用静态内部类
        //方式1
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        //方式2
        //编写一个方法,可以返回静态内部类的对象实例.
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("============");
        inner101.say();

        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        System.out.println("************");
        inner10_.say();
    }
}

class Outer10 { //外部类
    private int n1 = 10;
    private static String name = "张三";
    private static void cry() {}
    //Inner10就是静态内部类
    //1. 放在外部类的成员位置
    //2. 使用static 修饰
    //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    //5. 作用域 :同其他的成员,为整个类体
    static class Inner10 {
        private static String name = "韩顺平教育";
        public void say() {
            //如果外部类和静态内部类的成员重名时,静态内部类访问的时,
            //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
            System.out.println(name + " 外部类name= " + Outer10.name);
            cry();
        }
    }

    public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    public Inner10 getInner10() {
        return new Inner10();
    }

    public static Inner10 getInner10_() {
        return new Inner10();
    }
}
  • 35
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值