java基础之面向对象02

1.静态与非静态

在java类中,可用static修饰属性 ,方法,代码块,内部类。

被修饰后的成员具备以下特点:

  • 随着类的加载而加载
  • 优先于对象存在
  • 修饰的成员,被所有对象所共享
  • 访问权限允许时,可不创建对象,直接被类调用

类方法,类中使用static修饰的方法

  • 没有对象的实例时,可以用类名.方法名()的形式访问由static修饰的类方法。
  • 在static方法内部只能访问类的static修饰的属性或方法, 不能访问类的非static的结构
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有this。 (也不能有super ? YES!)
  • static修饰的方法不能被重写

案例:

public class Chinese {

    private String name;
    private int age;

    public static String nation = "大中国";

    public void f1() {
        System.out.println("f1");
    }

    public static void f2() {
        System.out.println("f2");
    }


    //自我介绍
    public static void show() {
        //在静态方法中,不能使用this,super
        //静态方法中,只能使用静态变量
        System.out.println("自我介绍" + nation);
        f2();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

测试

package k_static;

public class Test {
    public static void main(String[] args) {
        Chinese malong = new Chinese();
        malong.setName("马龙");
        malong.setAge(32);
        malong.nation = "中华人民共和国";

        Chinese yaoming = new Chinese();
        yaoming.setAge(34);
        yaoming.setName("姚明");

        yaoming.nation = "中国";

        System.out.println(malong.getName());
        System.out.println(malong.nation);
        System.out.println(Chinese.nation);
        Chinese.show();

    }
}

java基础之面向对象02_第1张图片

可以看到被static修饰的nation被修改后即使我们要输出马龙这个对象的nation也是最终修改成的中国。

需要注意的是:静态变量是与类本身关联的,因此它的值在整个类中是共享的。这意味着当一个静态变量的值被修改后,它对所有的实例对象和其他使用该静态变量的地方都会生效。这也是静态变量常用于表示全局或共享状态的原因之一

2.设计模式之单例模式

java基础之面向对象02_第2张图片

  • 设计模式:程序在某一个场景的,设计和开发套路,比如围棋和象棋套路
  • 单例模式:要求程序中某一个组件,在程序运行的整个生命周期中,只有一个实例
    • 构造函数私有,外面的组件不能主动创建这个对象
    • 提供静态方法返回对象实例,返回的是静态实例(静态的特性),所以只有一个实例

1.饿汉式单例模式

  • 好处:线程安全的
  • 坏处:加载时间比较长
public class Singleton1 {

    private Singleton1() {
        System.out.println("Singleton1 实例化了");
    }

    private static Singleton1 instance = new Singleton1(); //因为静态的特性只有一份存在方法区,所以这个对象一定只有一个

    public static Singleton1 getInstance() {
        return instance;
    }

}

java基础之面向对象02_第3张图片

2.懒汉式单例模式

存在线程安全问题,没有静态初始化

在常见的懒汉式实现中,如果多个线程同时调用获取实例的方法,并且实例还未被创建,则可能导致多个线程同时创建实例,从而违反了单例模式的要求。

package l_singleton;

public class Singleton2 {

    private Singleton2() {
        System.out.println("懒汉式单例模式实例化。。。。");
    }

    private static Singleton2 instance = null;

    public static Singleton2 getInstance() {
        if (instance == null) {
            instance = new Singleton2();
        }
        return instance;
    }

}

在上述代码中,如果多个线程同时通过getInstance()方法访问单例实例,当instance为null时,多个线程可能会并发地创建实例,导致返回多个不同的实例。

为了解决懒汉式的线程安全问题,可以使用加锁机制来保证在多线程环境下只创建一个实例。例如,可以使用synchronized关键字对获取实例的代码块进行同步,确保只有一个线程能够进入临界区创建实例。。以下是使用双重检查锁定(double check)实现的线程安全懒汉式示例:

public class Singleton {
    private static volatile Singleton instance;  // 使用volatile关键字修饰,禁止指令重排序
    
    private Singleton() {
        // 私有构造方法
    }
    
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

在上述代码中,通过双重检查锁定(Double-Checked Locking)的方式,在synchronized代码块内部再次检查实例是否已经被创建。这样可以保证只会在实例未被创建时才进行同步创建实例。

需要注意的是,在Java 5之后,使用volatile关键字修饰instance变量,可以禁止指令重排序,从而确保多线程环境下的正确性。

总的来说懒汉式在多线程环境下可能存在线程安全问题,可以通过加锁机制解决

3.代码块的初始化顺序

对象初始化顺序

  • 静态初始化
  • 初始化
  • 构造函数
  • 父类先执行
public class Parent {
    int i = 0; //初始化
    {
        System.out.println("Parent初始化代码块1");
    }
    {
        System.out.println("Parent初始化代码块2");
    }

    static int i2 = 0; //静态初始化
    static {
        System.out.println("Parent静态初始化代码块1");
    }
    static {
        System.out.println("Parent静态初始化代码块2");
    }

    public Parent() {
        System.out.println("Parent的构造函数。。。。");
    }
}

public class Children extends Parent {

    int i = 0; //初始化
    {
        System.out.println("Children初始化代码块1");
    }
    {
        System.out.println("Children初始化代码块2");
    }

    static int i2 = 0; //静态初始化
    static {
        System.out.println("Children静态初始化代码块1");
    }
    static {
        System.out.println("Children静态初始化代码块2");
    }

    public Children() {
        System.out.println("Children的构造函数。。。。");
    }

}


    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
//        Class clazz = (Class)Class.forName("m_order.Children");
//        Children bean = (Children) clazz.newInstance(); //反射产生对象
        Children c1 = new Children();
    }

结果是:

Parent静态初始化代码块1
Parent静态初始化代码块2
Children静态初始化代码块1
Children静态初始化代码块2
Parent初始化代码块1
Parent初始化代码块2
Parent的构造函数。。。。
Children初始化代码块1
Children初始化代码块2
Children的构造函数。。。。

可以看到在类加载之后就开始初始化静态代码块,然后先执行初始化父类的代码块,父类的构造函数,然后才是子类

这里可以说的是上面的代码中

  • 使用Class.forName(“className”)方法可以根据类名获取对应的Class对象。也就是加载class 不创建对象

  • 调用Class.newInstance()方法可以创建该类的一个实例对象。

4.final 关键词

  • 修饰类,表示类不能被继承
  • 修饰方法,表示方法不能被重写
  • 修饰成员变量,表示变量不能被改变

5.抽象类

  • 用abstract关键字来修饰一个类, 这个类叫做抽象类。
  • 用abstract来修饰一个方法, 该方法叫做抽象方法。抽象方法:只有方法的声明,没有方法的实现。以分号结束:比如: public abstract void talk();
  • 含有抽象方法的类必须被声明为抽象类。
  • 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
public abstract class A {

    public abstract void f1();

    public void f2() {
        System.out.println("f2");
    }
}


public class B extends A {

    @Override
    public void f1() {
        System.out.println("f1()");
    }

    //alt+enter:解决一切问题

}


public class Test {

    public static void main(String[] args) {
//        new A();
        new B();
    }

}

6.接口

  • 一方面, 有时必须从几个类中派生出一个子类, 继承它们所有的属性和方法。 但是, Java不支持多重继承。 有了接口, 就可以得到多重继承的效果。
  • 另一方面, 有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、 MP3机、手机、数码相机、移动硬盘等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。 继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
  • 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
public interface Usb {

    //接口的方法默认public abstract
    public abstract void start();

    void end();

    //接口中不能有普通方法
//    void f2() {
//    }

}


public interface TypeC {

    void transfer();

}


public class Printer implements Usb, TypeC {

    @Override
    public void start() {
        System.out.println("usb 启动");
    }

    @Override
    public void end() {
        System.out.println("usb 关闭");
    }

    public void transfer() {
        System.out.println("typeC接口传输数据");
    }
}

小节:接口和抽象的区别

  • 抽象类可以有非抽象方法,接口只能有抽象方法
  • 接口中方法默认 public abstract
  • 接口可以实现多继承,抽象类不可以

ps:但在java引入新特性中
接口中使用 default关键字也可以写入具体方法

7.内部类和匿名内部类

7.1 内部类

java中可以把一个类放到另一个类的内部,这个类成为内部类
内部类的分类:

  • 成员变量位置 成员内部类
    • static成员内部类
    • 非静态的成员内部类
  • 方法中,就是局部内部类

语法

  • 修饰成员变量的所有的修饰符都可以修饰成员内部类
  • 内部类可以使用继承
public class Person {

    private String name;
    private int age;

    //内部类,如果一个类只给当前外部类使用,别的组件不用,比如Dog和Cat类只是在Person中使用
    //所有的成员变量的修饰符,都可以修饰内部类
    public static class Dog {

        public void eat() {
            System.out.println("狗吃骨头");
        }
    }

    public class Cat {

        public void eat() {
            System.out.println("猫吃鱼");
        }

    }

    public void f1() {
        class Bird {
            public void eat() {
                System.out.println("鸟吃虫子");
            }
        }
        Bird b = new Bird();
        b.eat();
    }

}

测试

public class Test {

    public static void main(String[] args) {
        //创建静态成员内部类的方法
        Person.Dog dog = new Person.Dog();
        dog.eat();

        //创建非静态成员内部类的方法
        Person p = new Person();
        Person.Cat cat = p.new Cat();
        cat.eat();

        p.f1();

    }
}

7.2 匿名内部类

  • 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  • 匿名内部类的特点
    • 匿名内部类必须继承父类或实现接口
    • 匿名内部类只能有一个对象
    • 匿名内部类对象只能使用多态形式引用
  • 语法new 接口(类){实现};
public interface Usb {

    //接口的方法默认public abstract
    public abstract void start();

    void end();

}

public interface TypeC {

    void transfer();

}

测试代码

package s_anonymous;

public class Test {

    public static void f1(Usb usb) {
        usb.start();
        usb.end();
    }

    public static void f2(TypeC typeC) {
        typeC.transfer();
    }

    public static void main(String[] args) {
//        //内部类
//        class Test$1 implements Usb {
//            @Override
//            public void start() {
//                System.out.println("打印机usb启动");
//            }
//
//            @Override
//            public void end() {
//                System.out.println("打印机usb停止");
//            }
//        }
//        Test$1 usb1 = new Test$1();

        //方式1
        /**
         * 1:多态性:编译类型是父类
         * 2:new 接口(类),后面跟一个实现
         * 3:有一个对象usb1,实现了Usb接口的一个没有名字的类的实例
         *
         * Test$1->实现了Usb接口->用这个类产生一个对象usb1
         */
        Usb usb1 = new Usb() {
            @Override
            public void start() {
                System.out.println("打印机usb启动");
            }

            @Override
            public void end() {
                System.out.println("打印机usb停止");
            }
        };
        f1(usb1);


        //方式2:匿名内部类直接作为方法参数
        f2(new TypeC() {
            @Override
            public void transfer() {
                System.out.println("苹果14 正在传输数据....");
            }
        });

    }



}

其中在测试代码中的方法f1(Usb usb)和f2(TypeC typeC)传入的参数如果是他们的子接口,也是允许传入参数的,这里是有多态性的体现的。

你可能感兴趣的:(java,java,开发语言)