Java 第二阶段提升编程能力 【面向对象编程(高级部分)】

发布于:2023-02-10 ⋅ 阅读:(572) ⋅ 点赞:(0)

代码链接:https://download.csdn.net/download/qq_52354698/86263857?spm=1001.2014.3001.5503

1. 类变量和类方法

1. 什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

2. 如何定义类变量

访问修饰符 static 数据类型 变量名;
static 访问修饰符 数据类型 变量名;

3. 如何访问类变量

类变量的访问必须遵循其访问权限

类名.类变量名//类变量是随类的加载而创建的,即使没有创建对象实例也可以访问
对象名.类变量名

4. 类变量使用细节和注意事项

1. 什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name,fee)
2. 类变量与实例变量(普通属性)的区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
3. 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
4. 类变量可以通过 类名.类变量名 或者 对象名.类变量名 类访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【满足修饰符的访问权限和范围】
5. 实例变量不能通过 类名.类变量名 方式访问
6. 类变量是在类加载时就初始化了,也就说,即使没有创建对象,只要类加载了,就可以使用类变量了
7. 类变量的生命周期是随类的加载而开始,随着类的消亡而销毁

5. 类方法基本介绍

类方法也叫静态方法。

访问修饰符 static 数据返回类型 方法名() {}
static 访问修饰符 数据返回类型 方法名() {}

6. 类方法的调用

满足访问修饰符的访问权限和范围

类名.类方法名
对象名.类方法名

7. 类方法的使用场景

当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提供开发效率。

实际开发中,往往将一些通用的方法,设计成静态方法

8. 类方法使用细节和注意事项

  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区。
  2. 类方法中没有this参数
  3. 普通方法中国隐含着this的参数
  4. 类方法可以通过类名调用,也可以通过对象名调用
  5. 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
  6. 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
  7. 类方法(静态方法)中只能访问静态变量或静态方法。
  8. 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。

静态方法,只能访问静态的成员
非静态方法,可以访问静态成员和非静态成员

2. 理解main方法语法说明

    public static void main(String[] args) {
        
    }
  1. main方法是虚拟机调用
  2. java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
  3. java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
  4. 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
  5. java执行的程序 参数1 参数2 参数3 …
    在这里插入图片描述

特别提示

  1. 在mian()方法中,我们可以直接使用main()方法所在类的静态方法或静态属性。
  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。

3. 代码块

1. 基本介绍

代码化块又称为初始化块,属于类中成员 [ 即 是类的一部分 ],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

2. 基本语法

[修饰符] {
	代码
};

注意:

  1. 修饰符可选,要写的话,只能写 static
  2. 代码块分为两类,使用 static 修饰的叫静态代码块,没有 static 修饰的,叫普通代码块
  3. 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
  4. ; 好可以写上,也可以省略
package com.qdu.codeblock;

public class CodeBlock01 {

    public static void main(String[] args) {

        Movie movie = new Movie("大闹天宫");
        System.out.println("===============");
        Movie movie1 = new Movie("西天取经", "2002.12.12");

    }

}

class Movie {

    private String name;
    private String date;

    {
        System.out.println("广告1开始...");
        System.out.println("广告2开始...");
        System.out.println("广告3开始...");
    }

    public Movie(String name) {
        System.out.println("public Movie(String name) 被调用");
        this.name = name;
    }

    public Movie(String name, String date) {
        System.out.println("public Movie(String name, String date) 被调用");
        this.name = name;
        this.date = date;
    }
}

在这里插入图片描述

代码块的理解

  1. 相等于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
  2. 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提供代码的重用性

3. 代码块使用细节和注意事项

  1. static 代码块也叫静态代码块,作用就是对垒进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。
  2. 类什么时候被加载
    a. 创建对象实例时(new)
    b. 创建子类对象实例,父类也会被加载
    c. 使用类的静态成员时(静态属性,静态方法)
package com.qdu.codeblock;

public class CodeBlockDetail01 {

    public static void main(String[] args) {

        AA aa = new AA();

        System.out.println("=================");

        BB bb = new BB();

        System.out.println("=================");

        Cat cat = new Cat();

    }

}

class Cat {

    public static int n1 = 999;

    static {
        System.out.println("Cat 的静态代码块被执行...");
    }

}

class BB {

    static {
        System.out.println("BB 的静态代码块被执行...");
    }

}

class AA extends BB{

    static {
        System.out.println("AA 的静态代码块被执行...");
    }

}

在这里插入图片描述

  1. 普通的代码块,在创建对象实例时,会被隐式的调用。
    被创建一次,就会调用一次。
    如果只是使用类的静态成员时,普通代码块冰不会执行。

1. static 代码块是类加载时,执行,只会执行一次
2. 普通代码块是在创建对象时调用的,创建一次,调用一次
3. 类加载的3种情况,需要记住

  1. 创建一个对象时,在一个类,调用顺序是:
    a. 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
    b. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)

  2. 构造方法(构造器)的最前面其实隐含了 super() 和调用普通代码块

  3. 我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
    a. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    b. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
    c. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    d. 父类的构造方法
    e. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
    f. 子类的构造方法

  4. 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员

4. 单例设计模式

1. 什么是设计模式?

  1. 静态方法和属性的经典使用
  2. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。

2. 什么是单例模式

  1. 所谓类的单例设计模式,就是采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
  2. 单例模式有两种方式:a. 饿汉式,b. 懒汉式

3. 单例模式应用实例

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

饿汉式

package com.qdu.single_;

public class SingleTon01 {

    public static void main(String[] args) {

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

    }

}

class GirlFriend {

    private String name;

    private static GirlFriend girlFriend = new GirlFriend("小红");

    private GirlFriend(String name) {
        this.name = name;
    }

    public static GirlFriend getInstance() {
        return girlFriend;
    }

    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述

懒汉式

package com.qdu.single_;

public class SingleTon02 {

    public static void main(String[] args) {

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

    }

}

class Cat {

    private static String name;
    private static Cat cat;

    private Cat(String name) {
        this.name = name;
    }

    public static Cat getInstance() {

        if (cat == null) {
            cat = new Cat("小可爱");
        }
        return cat;

    }

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

在这里插入图片描述

4. 饿汉式VS懒汉式

  1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
  3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。

5. final关键字

1. 基本介绍

final中文的意思:最后的,最终的。
final可以修饰、属性、方法和局部变量。
在某些情况下,程序员可能会有一下需求,就会使用到final:

  1. 当不希望类被继承时,可以使用final修饰。
  2. 当不希望父类的某个方法被子类覆盖/重写时,可以使用final关键字修饰。
  3. 当不希望类的某个属性的值被修改,可以用final修饰。
  4. 当不希望某个局部变量被修改,可以使用final修饰。

2. final注意事项和使用细节

  1. final修饰的属性又叫常量,一般用XX_XX_XX来命名。
  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在以下位置之一:
    a. 定义时:如 public final double TAX_RATE = 0.08;
    b. 在构造器中
    c. 在代码块中
  3. 如果final修饰的属性是静态的,则初始化的位置只能是定义时、静态代码块中,不能在构造器中赋值。
  4. final类不能继承,但是可以实例化对象。
  5. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
  6. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法了。
  7. final不能修饰构造方法(即构造器)
  8. final和static往往搭配使用,效率更高,不会导致类加载,底层编译做了优化处理。
  9. 包装类(Integer、Double、Float、Boolean等都是final),String也是final类。

3. final实例

请编写一个程序,能够计算圆形的面积。要求圆周率为3.14。
在这里插入图片描述

package com.qdu.final_;

public class FinalExercise01 {

    public static void main(String[] args) {

        Circle circle = new Circle(2);
        System.out.println(circle.calArea());

    }

}

class Circle {

    private double radius;
    private final double PII = 3.14;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double calArea() {
        return PII * radius * radius;
    }
}

在这里插入图片描述

final修饰变量相当于常量,不能修改…

6. 抽象类

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

1. 基本介绍

  1. 用abstract关键字修饰一个类时,这个类就叫抽象类。
    访问修饰符 abstract 类名 {
    }

  2. 用abstract关键字来修饰一个方法时,这个方法就是抽象方法
    访问修饰符 abstract 返回类型 方法名(参数列表);

  3. 抽象类的价值更多是用于设计模式中,是设计者设计好后,让子类继承并实现抽象类()

2. 抽象类注意事项和使用细节

  1. 抽象类不能被实例化
  2. 抽象类不一定包含abstract方法,抽象类可以没有abstract方法
  3. 一个类一旦包含了abstract方法,则这个类一定要声明为abstract
  4. abstract只能修饰类和方法,不能修饰属性和其他的
  5. 抽象类可以有任意成员【抽象类还是类】
  6. 抽象方法不能有主体,不能实现
  7. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
  8. 抽象方法不能使用private、final、static来修饰,因为这些关键字都是喝重写相违背的

3. 抽象类的最佳实践

  1. 编写方法 calculateTime(),可以计算某段代码的耗时时间。
  2. 编写抽象方法 job()。
  3. 编写两个子类AA和BB,继承抽象类Template,并实现job方法。
  4. 编写一个测试类main。
    在这里插入图片描述
    mian类
package com.qdu.abstract_;

public class Abstract02 {

    public static void main(String[] args) {

        AA aa = new AA();
        aa.calculateTime();

        BB bb = new BB();
        bb.calculateTime();

    }

}

template类

package com.qdu.abstract_;

abstract class Template {

    public abstract void job();

    public void calculateTime() {
        long start = System.currentTimeMillis();
        job();
        long end = System.currentTimeMillis();
        System.out.println("执行时间 " + (end - start));
    }

}

AA类

package com.qdu.abstract_;

public class AA extends Template{

    @Override
    public void job() {
        long sum = 0;
        for (long i = 0; i <= 80000; i ++ ) {
            sum += i;
        }
    }

}

BB类

package com.qdu.abstract_;

public class BB extends Template{

    @Override
    public void job() {
        long sum = 0;
        for (long i = 0; i <= 100000; i ++ ) {
            sum += i;
        }
    }
}

7. 接口

1. 基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,根据具体的情况把这些方法写出来。

interface 接口名 {
	//属性
	//方法
}

class 类名 implements 接口 {
	自己属性;
	自己方法;
	//必须实现接口的抽象方法
}

在jdk7.0前,接口里所有的方法都没有方法体,即都是抽象方法。
在jdk8.0后,接口可以有静态方法,默认方法(需要使用default关键字修饰),就是说接口中可以有方法的具体实现(static修饰)

2. 接口的使用细节和注意事项

  1. 接口不能被实例化
  2. 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现
  4. 抽象类实现接口,可以不用实现接口的方法
  5. 一个类同时可以实现多个接口
  6. 接口中的属性只能是final的,而且是public static final修饰符
  7. 接口中的属性的访问修饰:接口名:属性名
  8. 一个接口不能继承其它的类,但是可以继承多个别的接口
  9. 接口的修饰符只能是public和默认,这点和类的修饰符是一样的

3. 接口VS继承类

当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式扩展
可以理解实现接口是对java单继承机制的一种补充

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

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

4. 接口的多态特性

  1. 多态参数:一个接口的参数可以接收不同的对象
  2. 多态数组:接口类型的数组
  3. 接口存在多态传递现象

8. 内部类

1. 基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。是我们类的第五大成员【类的五大成员:属性、方法、构造器、代码块、内部类】,内部类的最大特点是可以直接访问私有属性,并且可以体现类与类之间的包含关心。

2. 基本语法

class Outer {//外部类
	class Inner {//内部类
	}
}
class Other {//外部其他类
}

3. 内部类的分类

定义在外部类局部位置上(比如方法内):

  1. 局部内部类(有类名)
  2. 匿名内部类(没有类名)

定义在外部类的成员位置上:

  1. 成员内部类(没有static修饰)
  2. 静态内部类(使用static修饰)

4. 局部内部类的使用

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

  1. 可以直接方法外部类的所有成员,包括私有成员
  2. 不能添加访问修饰符,但是可以用final修饰
  3. 作用域:仅仅在定义它的方法或代码块中
  4. 局部内部类可以直接访问外部类的成员
  5. 外部类在方法中,可以创建局部内部类的对象,然后调用方法
  6. 外部其他类不能访问局部内部类
  7. 如果外部类和局部内部类的成员重名时,遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
package com.qdu.innerclass;

public class LocalInnerClass {

    public static void main(String[] args) {

        Outer outer = new Outer();
        outer.m1();

    }

}

class Outer {

    private int n = 100;
    private void m2() {
        System.out.println("Outer m2()");
    }

    public void m1() {

        final class Inner {
            private int n = 800;
            public void f1() {
                System.out.println("n = " + n + "\n外部类的 n = " + Outer.this.n);
                m2();
            }
        }
        Inner inner = new Inner();
        inner.f1();

    }

}

在这里插入图片描述

5. 匿名内部类的使用

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

基本语法

new 类或接口(参数列表){
	类体;
};

基于接口的匿名内部类

package com.qdu.innerclass;

public class AnonymousInnerClass {

    public static void main(String[] args) {

        Outer04 outer04 = new Outer04();
        outer04.method();

    }

}

class Outer04 {

    private int n1 = 10;

    public void method() {

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

        IA tiger1 = new IA(){
            @Override
            public void cry() {
                System.out.println("老虎又叫唤...");
            }
        };
        tiger1.cry();

    }
}

interface IA {

    public void cry();
}

class Tiger implements IA {

    @Override
    public void cry() {
        System.out.println("老虎叫唤...");
    }
}

class Father {

    public Father(String name) {
        super();
    }

    public void test() {

    }
}

在这里插入图片描述
基于类的匿名内部类

        Father father = new Father("jack") {
            @Override
            public void test() {
                System.out.println("重写了Father的test方法");
            }
        };
        father.test();

6. 使用细节和注意事项

  1. 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
        Person person = new Person() {
            @Override
            public void h1() {
                System.out.println("匿名内部类重写了 hi() 方法 ... ");
            }
        };
        person.h1();
        new Person() {
            @Override
            public void h1() {
                System.out.println("匿名内部类重写了 hi() 方法 ... 哈哈哈哈哈哈!!!");
            }
        }.h1();
  1. 可以直接访问外部类的所有成员,包括私有成员
  2. 不能添加访问修饰符,因为它的地位就是一个局部变量
  3. 作用域:仅仅在定义它的方法或代码块中
  4. 匿名内部类访问外部成员【直接访问】
  5. 外部其他类不能访问匿名内部类(因为匿名内部类的地位是一个局部变量)
  6. 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问

7. 成员内部类

成员内部类是定义在外部类的成员位置,并且没有static修饰

  1. 可以直接访问外部类的所有成员,包含私有的。
  2. 可以添加任意的访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
  3. 作用域:和外部类的其他成员一样,为整个类体;在外部类的成员方法中创建成员内部类对象,再调用方法。
  4. 成员内部类 访问 外部类成员【直接访问】。
  5. 外部类 访问 成员内部类【创建对象,再访问】。
  6. 外部其他类 访问 成员内部类。
  7. 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。

8. 静态内部类

静态内部类是定义在外部类的成员位置,并且有static修饰

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
  2. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
  3. 作用域:同其他的成员,为整个类体。
  4. 静态内部类 访问 外部类【直接访问】。
  5. 外部类 访问 静态内部类【先创建对象,再访问】。
  6. 其他外部类 访问 静态内部类。
  7. 如果外部类和静态内部类的成员重名时,静态内部类访问的时候,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。

我亦无他,唯手熟尔


网站公告

今日签到

点亮在社区的每一天
去签到