Java06-面向对象02抽象类接口

NiuMT 2020-06-03 20:58:30
Java

static 关键字

有时候希望无论是否产生了对象或无论产生了多少对象的情况下, 某些特定的数据在内存空间里只有一份。

static可以修饰属性、方法、代码块、内部类。

  1. 随着类的加载而加载,由于类只会加载一次,则静态变量在内存中也只会存在一份,存放在方法区的静态域中。
  2. 优先于对象存在
  3. 修饰的成员,被所有对象所共享
  4. 访问权限允许时,可不创建对象,直接被类调用
  5. 类属性前面省略的是类名,而不是this

被static修饰后的方法:

  1. 静态方法中,只能调用静态的方法或属性;非静态方法都可以调用
  2. 在静态方法内,补鞥呢使用this关键字、super关键字
  3. static修饰的方法不能被重写
  4. 操作静态属性的方法,通常设置为static

单例(Singleton)设计模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例 ,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的==构造器的访问权限设置为 private==,这样,就不能用 new 操作符在类的外部产生类的对象了,但在==类内部仍可以产生该类的对象==。因为在类的外部开始还无法得到类的对象,==只能====调用该类的某个静态方法==以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的==该类对象的变量也必须定义成静态的==。

// 饿汉式
class Singleton {
    // 1. 私有化构造器
    private Singleton() {
    }

    // 2. 内部提供一个当前类的实例
    // 4. 此实例也必须静态化
    private static Singleton single = new Singleton();

    // 3. 提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
        return single;
    }
}

// 饿汉式
class Bank{
    private Bank(){}

    public static final Bank instance = new Bank();
}
// 懒汉式
// 懒汉式暂时还存在线程安全问题,讲到多线程时,可修复
class Singleton {
    // 1. 私有化构造器
    private Singleton() {
    }

    // 2. 声明当前类对象,没有初始化
    // 4. 此实例也必须静态化
    private static Singleton single = null;

    //3. 提供公共的静态的方法,返回当前类的对象
    public static Singleton getInstance() {
    if (single == null )
        single = new Singleton();
        }
    return single;
    }
}

懒汉式:好处,延迟对象创建;坏处,目前不安全。

饿汉式:好处,是线程安全的;坏处,对象加载时间过长;

单例模式的优点:由于单例模式只生成一个实例,==减少了系统性能开销==,当一个对象的产生 需要比较 多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来 解决。

main()方法说明

  1. 作为程序的入口
  2. 也是一个普通的静态方法
  3. 可以作为与控制台交互的方式,参数为String类型

代码块 (初始化块)

  1. 代码块或初始化块的作用:对 Java 类或对象进行初始化
  2. 一个类中代码块若有修饰符则只能被 static 修饰,称为静态代码块(static block) ;没有使用static 修饰的为非静态代码块。
  3. 静态代码块随着类的加载而执行,而且只执行一次,通常用于初始化 static 的属性。
  4. 非静态代码块随着类的创建而执行,每次创建均执行。可以对属性初始化。

静态代码块:用 static 修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行 一 次。

非静态代码块:没有 static 修饰的代码块

  1. 可以有输出语句 。
  2. 可以对类的属性 、 类的声明进行初始化操作 。
  3. 除了调用非静态的结构外还可以调用静态的变量或方法 。
  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行 。
  5. 每次创建对象的时候都会执行一次 。 且先于构造器执行 。

image-20201012224406043

class Root{
    static{
        System.out.println("Root的静态初始化块");
    }
    {
        System.out.println("Root的普通初始化块");
    }
    public Root(){
        System.out.println("Root的无参数的构造器");
    }
}
class Mid extends Root{
    static{
        System.out.println("Mid的静态初始化块");
    }
    {
        System.out.println("Mid的普通初始化块");
    }
    public Mid(){
        System.out.println("Mid的无参数的构造器");
    }
    public Mid(String msg){
        //通过this调用同一类中重载的构造器
        this();
        System.out.println("Mid的带参数构造器,其参数值:" + msg);
    }
}
class Leaf extends Mid{
    static{
        System.out.println("Leaf的静态初始化块");
    }
    {
        System.out.println("Leaf的普通初始化块");
    }    
    public Leaf(){
        //通过super调用父类中有一个字符串参数的构造器
        super("尚硅谷");
        System.out.println("Leaf的构造器");
    }
}
public class LeafTest{
    public static void main(String[] args){
        new Leaf(); 
        System.out.println();
        new Leaf();
    }
}

/*
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器

Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
*/
class Father {
    static {
        System.out.println("11111111111");
    }
    {
        System.out.println("22222222222");
    }

    public Father() {
        System.out.println("33333333333");
    }
}

public class Son extends Father {
    static {
        System.out.println("44444444444");
    }
    {
        System.out.println("55555555555");
    }
    public Son() {
        System.out.println("66666666666");
    }

    public static void main(String[] args) { // 由父及子 静态先行
        System.out.println("77777777777");
        System.out.println("1************************");
        new Son();
        System.out.println("2************************");

        new Son();
        System.out.println("3************************");
        new Father();
    }

}

/*
11111111111
44444444444
77777777777
1************************
22222222222
33333333333
55555555555
66666666666
2************************
22222222222
33333333333
55555555555
66666666666
3************************
22222222222
33333333333
*/

final 关键字

可以修饰类、方法、变量

public class Something {
    public int addOne( final int x )
    return ++x; // 报错,x无法修改
    // return x + 1; // 正确
    }
}
public class Something {
    public static void main(String[] args )
        Other o = new Other();
        new Something().addOne(o);
    }
    public void addOne( final Other o )
        // o = new Other();  // 报错,形参o的地址无法修改,内部属性可以修改
        o.i++; // 正确
    }
}
class Other {
    public int i;
}

抽象类与抽象方法

  1. 用 abstract 关键字来修饰一个类, 这个类叫做抽象类 。
    • 此类不能实例化
    • 抽象类一定有构造器,便于子类实例化调用
    • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作。
  2. 用 abstract 来修饰一个方法, 该方法叫做抽象方法 。
    • 抽象方法:只有方法的声明,没有方法的实现。以分号结束:比如:public abstract void talk()
    • 含有抽象方法的类必须被声明为抽象类。
  3. 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。==若没有重写全部的抽象方法,仍为抽象类==。
  4. 不能用 abstract 修饰变量、代码块、构造器;
  5. 不能用 abstract 修饰 私有方法、静态方法、 final 的方法、 final 的类。

abstract注意点:

是否可以这样理解:抽象类就是比普通类多定义了抽象方法 ,除了不能直接进行类的实例化操作之外,并没有任何的不同?对~

模板方法设计模式 TemplateMethod

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

解决的问题:

模板方法设计模式是编程中经常用得到的模式 。 各个框架 、 类库中都有他的影, 比如常见的有:

接口

一方面 有时 必须从几个类中派生出一个子类 继承它们所有的属性和方法 。 但是 Java 不支持多重继承 。 有了接口就可以得到多重继承的 效果 。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是 要 则必须能 ……”的思想。 ==继承是一个是不是的关系,而接口实现则是能不能的关系==。

==接口的本质是契约,标准,规范==,就像我们的法律一样。制定好后大家都要遵守。

JDK7.0 接口 interface 是==抽象方法==和==常量值==定义的集合 。

JDK8.0 还可以定义静态方法,默认方法:

  1. 接口中定义的静态方法,只能通过接口来调用。
    1. 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。
      我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。重写接口中的默认方法,调用的为重写后的方法。
      1. 类优先原则:如果子类(实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,在子类没有重写的情况下,默认调用父类的方法。
      2. 接口冲突:如果实现类实现了多个接口,而多个接口存在同名同参数的默认方法,在实现类没有重写的情况下,会出现接口冲突;这就需要必须重写此方法。
      3. 调用接口中被重写的方法。image-20201014144352158

接口的特点:

interface A {
    int x = 1;
}
class B {
    int x = 1;
}
class C extends B implements A {
    public void pX() {
        // System.out.println(x);// 编译不通过
        System.out.println(super.x);// 1
        System.out.println(A.x);// 0
    }
    public static void main(String[] args ){
        new C().pX();
    }
}
interface Playable {
    void play();
}
interface Bounceable {
    void play();
}
interface Rollable extends Playable, Bounceable {
    Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
    private String name;
    public String getName() {
        return name;
    }
    public Ball(String name )
        this.name = name;
    }
    public void play() {
        ball = new Ball("Football");// ball 是 static final修饰的,无法重新赋值
        System.out.println(ball.getName());
    }
}

image-20201014140223224

内部类

成员内部类:

实例化成员内部类的对象
// 静态的成员内部类
Person.Dog dog = new Person.Dog();

// 非静态的成员内部类
Persion p = new Person();
Person.Bird bir = p.new Bird();

注意:

1.
非 static 的成员内部类中的成员不能声明为 static 的,只有在外部类或 static 的成员内部类中才可声明 static 成员 。

2.
外部类访问成员内部类的成员需要”内部类.成员”或”内部类对象.成员”的方式

3.
成员内部类可以直接使用外部类的所有成员 包括私有的数据

4.
当想要在外部类的静态成员部分使用内部类时可以考虑内部类声明为静态的

抽象类可以实现接口。抽象类可以继承非抽象的类。

在局部内部类的方法中,如果调用外部类声明的方法中局部变量,则要求此局部变量声明final

  • JDK8.0可以省略final关键字。

    image-20201014165848459