Java面向对象(高级)

1、static关键字修饰属性和方法


static关键字的使用

1. static: 静态的

2. static 用来修饰的结构:属性、方法; 代码块、内部类;

3. static修饰属性
   3.1 复习:变量的分类
   方式1:按照数据类型:基本数据类型、引用数据类型

   方式2:按照类中声明的位置:
        成员变量:按照是否使用static修饰进行分类:
            使用static修饰的成员变量:静态变量、类变量
            不使用static修饰的成员变量:非静态变量、实例变量

        局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等。

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

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


        Chinese c1 = new Chinese();
        c1.name = "姚明";
        c1.age = 40;
        c1.nation = "China";

        Chinese c2 = new Chinese();
        c2.name = "刘翔";
        c2.age = 39;

        System.out.println(c1);
        System.out.println(c2);

        System.out.println(c1.nation);//China
        System.out.println(c2.nation);//China

        c2.nation = "CHN";

        System.out.println(c1.nation);//CHN
        System.out.println(c2.nation);//CHN

        c1.show();

        ChineseTest.test();
    }

    public static void test(){
        System.out.println("我是static的测试方法");
    }
}

class Chinese{ //中国人类
    //非静态变量、实例变量
    String name;
    int age;

    //静态变量、类变量
    static String nation = "中国";

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

    public void eat(String food){
        System.out.println("我喜欢吃" + food);
    }

    public static void show(){
        System.out.println("我是一个中国人");

        //调用静态的结构
        System.out.println("nation = " + Chinese.nation);
        method1();

        //调用非静态的结构
//        System.out.println("name = " + this.name);
//        this.eat("饺子");
    }

    public static void method1(){
        System.out.println("我是静态的测试方法");
    }

    public void method2(){
        System.out.println("我是非静态的测试方法");
        //调用非静态的结构
        System.out.println("name = " + this.name);
        this.eat("饺子");

        //调用静态的结构
        System.out.println("nation = " + nation);
        method1();
    }

    public static String getNation() {
        return nation;
    }

    public static void setNation(String nation) {
        Chinese.nation = nation;
    }
}


   3.2 静态变量:类中的属性使用static进行修饰。
       对比静态变量与实例变量:
       ① 个数
        >静态变量:在内存空间中只有一份,被类的多个对象所共享。
        >实例变量:类的每一个实例(或对象)都保存着一份实例变量。
       ② 内存位置
        >静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
        >实例变量:存放在堆空间的对象实体中。
       ③ 加载时机
        >静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。
        >实例变量:随着对象的创建而加载。每个对象拥有一份实例变量。
       ④ 调用者
        >静态变量:可以被类直接调用,也可以使用对象调用。
        >实例变量:只能使用对象进行调用。
       ⑤ 判断是否可以调用 ---> 从生命周期的角度解释
                    类变量         实例变量
        类            yes            no
        对象          yes            yes

       ⑥ 消亡时机
        >静态变量:随着类的卸载而消亡
        >实例变量:随着对象的消亡而消亡

4. static修饰方法:(类方法、静态方法)

> 随着类的加载而加载
> 可以通过“类.静态方法”的方式,直接调用静态方法
> 静态方法内可以调用静态的属性或静态的方法。(属性和方法的前缀使用的是当前类,可以省略)
          不可以调用非静态的结构。(比如:属性、方法)

>                     类方法         实例方法
          类            yes            no
          对象          yes            yes
> static修饰的方法内,不能使用this和super

> 补充:在类的非静态方法中,可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)

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

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


        Chinese c1 = new Chinese();
        c1.name = "姚明";
        c1.age = 40;
        c1.nation = "China";

        Chinese c2 = new Chinese();
        c2.name = "刘翔";
        c2.age = 39;

        System.out.println(c1);
        System.out.println(c2);

        System.out.println(c1.nation);//China
        System.out.println(c2.nation);//China

        c2.nation = "CHN";

        System.out.println(c1.nation);//CHN
        System.out.println(c2.nation);//CHN

        c1.show();

        ChineseTest.test();
    }

    public static void test(){
        System.out.println("我是static的测试方法");
    }
}

class Chinese{ //中国人类
    //非静态变量、实例变量
    String name;
    int age;

    //静态变量、类变量
    static String nation = "中国";

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

    public void eat(String food){
        System.out.println("我喜欢吃" + food);
    }

    public static void show(){
        System.out.println("我是一个中国人");

        //调用静态的结构
        System.out.println("nation = " + Chinese.nation);
        method1();

        //调用非静态的结构
//        System.out.println("name = " + this.name);
//        this.eat("饺子");
    }

    public static void method1(){
        System.out.println("我是静态的测试方法");
    }

    public void method2(){
        System.out.println("我是非静态的测试方法");
        //调用非静态的结构
        System.out.println("name = " + this.name);
        this.eat("饺子");

        //调用静态的结构
        System.out.println("nation = " + nation);
        method1();
    }

    public static String getNation() {
        return nation;
    }

    public static void setNation(String nation) {
        Chinese.nation = nation;
    }
}


5. 开发中,什么时候需要将属性声明为静态的?
    > 判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的。
    > 开发中,常将一些常量声明是静态的。比如:Math类中的PI


   什么时候需要将方法声明为静态的?
    > 方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法
    > 开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类

2、static的应用举例及练习

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

        Circle c1 = new Circle();
        System.out.println(c1);

        Circle c2 = new Circle();
        System.out.println(c2);

        Circle c3 = new Circle();
        System.out.println(c3);

        Circle c4 = new Circle(2.3);
        System.out.println(c4);

        System.out.println(Circle.total);

    }
}

class Circle{

    double radius;//实例变量

    int id;//编号

    static int total;//创建的Circle实例的个数


    public Circle(){
        this.id = init;
        init++;
        total++;
    }

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

    }

    private static int init = 1001; //自动给id赋值的基数

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                ", id=" + id +
                '}';
    }
}

案例1:

编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,
定义封装这些属性的方法。账号要自动生成。

编写主类,使用银行账户类,输入、输出3个储户的上述信息。

考虑:哪些属性可以设计成static属性。

public class Account {

    private int id; //账号

    private String password;//密码

    private double balance; //余额

    private static double interestRate;//利率

    private static double minBalance = 1.0;//最小余额

    private static int init = 1001;//用于自动生成id的基数

    public Account() {
        this.id = init;
        init++;
        password = "000000";
    }

    public Account(String password, double balance) {
        this.password = password;
        this.balance = balance;
        this.id = init;
        init++;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public static double getInterestRate() {
        return interestRate;
    }

    public static void setInterestRate(double interestRate) {
        Account.interestRate = interestRate;
    }

    public static double getMinBalance() {
        return minBalance;
    }

    public static void setMinBalance(double minBalance) {
        Account.minBalance = minBalance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", password='" + password + '\'' +
                ", balance=" + balance +
                '}';
    }
}
public class AccountTest {
    public static void main(String[] args) {

        Account acct1 = new Account();
        System.out.println(acct1);

        Account acct2 = new Account("123456",2000);
        System.out.println(acct2);

        Account.setInterestRate(0.0123);
        Account.setMinBalance(10);

        System.out.println("银行存款的利率为:" + Account.getInterestRate());
        System.out.println("银行最小存款额度为:" + Account.getMinBalance());
    }
}

案例2:
自定义一个数组的工具类,封装常用的数组算法

public class MyArrays {

    /**
     * 获取int[]数组的最大值
     * @param arr 要获取最大值的数组
     * @return 数组的最大值
     */
    public static int getMax(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(max < arr[i]){
                max = arr[i];
            }
        }
        return max;
    }

    /**
     * 获取int[]数组的最小值
     * @param arr 要获取最小值的数组
     * @return  数组的最小值
     */
    public static int getMin(int[] arr){
        int min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(min > arr[i]){
                min = arr[i];
            }
        }
        return min;
    }

    public static int getSum(int[] arr){
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }

    public static int getAvg(int[] arr){

        return getSum(arr) / arr.length;
    }

    public static void print(int[] arr){ //[12,231,34]
        System.out.print("[");

        for (int i = 0; i < arr.length; i++) {
            if(i == 0){
                System.out.print(arr[i]);
            }else{
                System.out.print("," + arr[i]);
            }
        }

        System.out.println("]");
    }

    public static int[] copy(int[] arr){
        int[] newArr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        return newArr;
    }

    public static void reverse(int[] arr){
        for(int i = 0,j = arr.length - 1;i < j;i++,j--){
            //交互arr[i] 与 arr[j]位置的元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    public static void sort(int[] arr){
        for(int j = 0;j < arr.length - 1;j++){
            for (int i = 0; i < arr.length - 1 - j; i++) {
                if(arr[i] > arr[i + 1]){
                    //交互arr[i] 和 arr[i + 1]
                    int temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                }

            }
        }
    }

    /**
     * 使用线性查找的算法,查找指定的元素
     * @param arr 待查找的数组
     * @param target 要查找的元素
     * @return target元素在arr数组中的索引位置。若未找到,则返回-1
     */
    public static int linearSearch(int[] arr,int target){

        for(int i = 0;i < arr.length;i++){
            if(target == arr[i]){
                return i;
            }

        }

        //只要代码执行到此位置,一定是没找到
        return -1;

    }



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

        int[] arr = new int[]{34,56,223,2,56,24,56,67,778,45};

        //求最大值
        System.out.println("最大值为:" + MyArrays.getMax(arr));
        //求平均值
        System.out.println("平均值为:" + MyArrays.getAvg(arr));

        //遍历
        MyArrays.print(arr);

        //查找
        int index = MyArrays.linearSearch(arr,24);
        if(index >= 0){
            System.out.println("找到了,位置为:" + index);
        }else{
            System.out.println("未找到");
        }

        //排序
        MyArrays.sort(arr);
        //遍历
        MyArrays.print(arr);

    }
}

3、单例设计模式与mai()的理解

1. 设计模式概述:
设计模式是在大量的`实践中总结`和`理论化`之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。
就像是经典的棋谱,不同的棋局,我们用不同的棋谱。"套路"

经典的设计模式一共有23种。

2. 何为单例模式(Singleton):
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

3. 如何实现单例模式(掌握):

> 饿汉式

public class BankTest {
    public static void main(String[] args) {
//        Bank bank1 = new Bank();
//        Bank bank2 = new Bank();

        Bank bank1 = Bank.getInstance();
        Bank bank2 = Bank.getInstance();

        System.out.println(bank1 == bank2);

    }
}

//饿汉式
class Bank{

    //1. 类的构造器私有化
    private Bank(){

    }

    //2. 在类的内部创建当前类的实例
    //4. 此属性也必须声明为static的
    private static Bank instance = new Bank();

    //3. 使用getXxx()方法获取当前类的实例,必须声明为static的
    public static Bank getInstance(){
        return instance;
    }

}

> 懒汉式

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

    }
}

//懒汉式
class GirlFriend{
    //1.类的构造器私有化
    private GirlFriend(){
    }

    //2. 声明当前类的实例
    //4. 此属性也必须声明为static的
    private static GirlFriend instance = null;

    //3. 通过getXxx()获取当前类的实例,如果未创建对象,则在方法内部进行创建
    public static GirlFriend getInstance(){

        if(instance == null){

            instance = new GirlFriend();

        }

        return instance;


    }
}


4. 对比两种模式(特点、优缺点)
特点:
 > 饿汉式:“立即加载”,随着类的加载,当前的唯一实例就创建了
 > 懒汉式:"延迟加载",在需要使用的时候,进行创建。

优缺点:
 > 饿汉式:(优点)写法简单,由于内存中较早加载,使用更方便、更快。是线程安全的。 (缺点)内存中占用时间较长。
 > 懒汉式:(缺点)线程不安全 (放到多线程章节时解决)(优点)在需要的时候进行创建,节省内存空间。

main()方法的剖析
public static void main(String args[]){}

1. 理解1:看做是一个普通的静态方法
   理解2:看做是程序的入口,格式是固定的。

2. 与控制台交互
如何从键盘获取数据?
>方式1:使用Scanner
>方式2:使用main()的形参进行传值。

public class MainTest {
    public static void main(String[] args) { //程序的入口
        String[] arr = new String[]{"AA","BB","CC"};
        Main.main(arr);
    }
}

class Main{

    public static void main(String[] args) { //看做是普通的静态方法
        System.out.println("Main的main()的调用");
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }

    }

}


 

4、代码块

类的成员之四:代码块

回顾:类中可以声明的结构:属性、方法、构造器;代码块(或初始化块)、内部类

1. 代码块(或初始化块)的作用:
用来初始化类或对象的信息(即初始化类或对象的成员变量)

2. 代码块的修饰:
  只能使用static进行修饰。

3. 代码块的分类:
  静态代码块:使用static修饰
  非静态代码块:没有使用static修饰

4. 具体使用:
4.1 静态代码块:
    > 随着类的加载而执行
    > 由于类的加载只会执行一次,进而静态代码块的执行,也只会执行一次
    > 作用:用来初始化类的信息
    > 内部可以声明变量、调用属性或方法、编写输出语句等操作。
    > 静态代码块的执行要先于非静态代码块的执行
    > 如果声明有多个静态代码块,则按照声明的先后顺序执行
    > 静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态的属性、方法)

4.2 非静态代码块:
    > 随着对象的创建而执行
    > 每创建当前类的一个实例,就会执行一次非静态代码块
    > 作用:用来初始化对象的信息
    > 内部可以声明变量、调用属性或方法、编写输出语句等操作。
    > 如果声明有多个非静态代码块,则按照声明的先后顺序执行
    > 非静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态的属性、方法)

public class BlockTest {
    public static void main(String[] args) {
        System.out.println(Person.info);
        System.out.println(Person.info);


        Person p1 = new Person();
        Person p2 = new Person();
        System.out.println(p1.age);//1
//        p1.eat();
    }
}

class Person{

    String name;
    int age;

    static String info = "我是一个人";

    public void eat(){
        System.out.println("人吃饭");
    }

    public Person(){}

    //非静态代码块
    {
        System.out.println("非静态代码块2");
    }
    {
        System.out.println("非静态代码块1");
        age = 1;
        System.out.println("info = " + info);
    }


    //静态代码块
    static{
        System.out.println("静态代码块2");
    }
    static{
        System.out.println("静态代码块1");
        System.out.println("info = " + info);
//        System.out.println("age = " + age);
//        eat();
    }



}

案例1:

(1)声明User类,

- 包含属性:userName(String类型),password(String类型),registrationTime(long类型),私有化

- 包含get/set方法,其中registrationTime没有set方法

- 包含无参构造,
  - 输出“新用户注册”,
  - registrationTime赋值为当前系统时间,
  - userName就默认为当前系统时间值,
  - password默认为“123456”

- 包含有参构造(String userName, String password),
  - 输出“新用户注册”,
  - registrationTime赋值为当前系统时间,
  - username和password由参数赋值

- 包含public String getInfo()方法,返回:“用户名:xx,密码:xx,注册时间:xx”

(2)编写测试类,测试类main方法的代码

public class User {
    private String userName;
    private String password;
    private long registrationTime;//注册时间

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public long getRegistrationTime() {
        return registrationTime;
    }

    public User(){
        System.out.println("新用户注册");
        registrationTime = System.currentTimeMillis();//获取系统当前时间 (距离1970-1-1 00:00:00的毫秒数)
        userName = System.currentTimeMillis() + "";
        password = "123456";
    }

    public User(String userName,String password){
        System.out.println("新用户注册");
        registrationTime = System.currentTimeMillis();
        this.userName = userName;
        this.password = password;
    }

    public String getInfo(){
        return "用户名:" + userName + ", 密码:" + password + ",注册时间:" + registrationTime;
    }
}
public class User1 {
    private String userName;
    private String password;
    private long registrationTime;//注册时间

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public long getRegistrationTime() {
        return registrationTime;
    }

    {
        System.out.println("新用户注册");
        registrationTime = System.currentTimeMillis();//获取系统当前时间 (距离1970-1-1 00:00:00的毫秒数)
    }

    //代码块的使用
    public User1(){

        userName = System.currentTimeMillis() + "";
        password = "123456";
    }

    public User1(String userName,String password){
//        System.out.println("新用户注册");
//        registrationTime = System.currentTimeMillis();
        this.userName = userName;
        this.password = password;
    }

    public String getInfo(){
        return "用户名:" + userName + ", 密码:" + password + ",注册时间:" + registrationTime;
    }
}
public class UserTest {
    public static void main(String[] args) {
        User u1 = new User();
        System.out.println(u1.getInfo());

        User u2 =  new User("Tom","654321");
        System.out.println(u2.getInfo());

        System.out.println();
        User1 u3 = new User1();
        System.out.println(u3.getInfo());

    }
}

5、类中属性赋值的位置及过程

1. 可以给类的非静态的属性(即实例变量)赋值的位置有:
① 默认初始化
② 显式初始化  或 ⑤ 代码块中初始化
③ 构造器中初始化
***********************
④ 有了对象以后,通过"对象.属性"或"对象.方法"的方法进行赋值


2. 执行的先后顺序:
① - ②/⑤ - ③ - ④

3. (超纲)关于字节码文件中的<init>的简单说明:(通过插件jclasslib bytecode viewer查看)

> <init>方法在字节码文件中可以看到。每个<init>方法都对应着一个类的构造器。(类中声明了几个构造器就会有几个<init>)
> 编写的代码中的构造器在编译以后就会以<init>方法的方式呈现
> <init>方法内部的代码包含了实例变量的显示赋值、代码块中的赋值和构造器中的代码。
> <init>方法用来初始化当前创建的对象的信息的。


4. 给实例变量赋值的位置很多,开发中如何选?

> 显示赋值:比较适合于每个对象的属性值相同的场景

> 构造器中赋值:比较适合于每个对象的属性值不相同的场景

由父及子 静态先行

public class FinalTest {
    public static void main(String[] args) {
        E e = new E();
        System.out.println(e.MIN_SCORE);
//        e.MIN_SCORE = 1;

        E e1 = new E(10);
//        e1.LEFT = 11;
    }
}

class E{
    //成员变量
    final int MIN_SCORE = 0;
    final int MAX_SCORE;

    final int LEFT;

//    final int RIGHT;

    {
//        MIN_SCORE = 1;
        MAX_SCORE = 100;
    }

    public E(){
        LEFT = 2;
    }
    public E(int left){
        LEFT = left;
    }

//    public void setRight(int right){
//        RIGHT = right;
//    }

}

class F{
    public void method(){
        final int num;
        num = 10;
//        num++;
        System.out.println(num);
    }

    public void method(final int num){
//        num++;
        System.out.println(num);
    }
}


final class A{

}

//class B extends A{}

//class SubString extends String{}

class C{
    public final void method(){

    }
}

class D extends C{
//    public void method(){
//
//    }
}

6、final修饰的使用


final关键字的使用

1. final的理解:最终的

2. final可以用来修饰的结构:类、方法、变量

3. 具体说明:

3.1 final修饰类:表示此类不能被继承。
    比如:String、StringBuffer、StringBuilder类

3.2 final修饰方法:表示此方法不能被重写
    比如:Object类中的getClass()

3.3 final修饰变量:既可以修饰成员变量,也可以修饰局部变量。
    此时的"变量"其实就变成了"常量",意味着一旦赋值,就不可更改。

    3.3.1 final修饰成员变量: 有哪些位置可以给成员变量赋值?
        > 显式赋值
        > 代码块中赋值
        > 构造器中赋值

   3.3.2 final修饰局部变量:一旦赋值就不能修改
        > 方法内声明的局部变量:在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改
        > 方法的形参:在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改

4. final与static搭配:修饰成员变量时,此成员变量称为:全局常量。
   比如:Math的PI

public class FinalTest {
    public static void main(String[] args) {
        E e = new E();
        System.out.println(e.MIN_SCORE);
//        e.MIN_SCORE = 1;

        E e1 = new E(10);
//        e1.LEFT = 11;
    }
}

class E{
    //成员变量
    final int MIN_SCORE = 0;
    final int MAX_SCORE;

    final int LEFT;

//    final int RIGHT;

    {
//        MIN_SCORE = 1;
        MAX_SCORE = 100;
    }

    public E(){
        LEFT = 2;
    }
    public E(int left){
        LEFT = left;
    }

//    public void setRight(int right){
//        RIGHT = right;
//    }

}

class F{
    public void method(){
        final int num;
        num = 10;
//        num++;
        System.out.println(num);
    }

    public void method(final int num){
//        num++;
        System.out.println(num);
    }
}


final class A{

}

//class B extends A{}

//class SubString extends String{}

class C{
    public final void method(){

    }
}

class D extends C{
//    public void method(){
//
//    }
}

7、抽象类与抽象方法的使用


抽象类与抽象方法


1. 案例引入
举例1:GeometricObject-Circle-Rectangle

abstract class GeometricObject{  //几何图形

    //求面积 (只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)

    //求周长(只能考虑提供方法的声明,而没有办法提供方法体。所以,此方法适合声明为抽象方法)

}

class Circle extends GeometricObject{

    //求面积 (必须重写(或实现)父类中的抽象方法)

    //求周长(必须重写(或实现)父类中的抽象方法)
}

class Rectangle extends GeometricObject{

    //求面积 (必须重写(或实现)父类中的抽象方法)

    //求周长(必须重写(或实现)父类中的抽象方法)
}


举例2:Account-SavingAccount-CheckAcount

abstract class Account{

    double balance;//余额

    //取钱 (声明为抽象方法)

    //存钱 (声明为抽象方法)

}

class SavingAccount extends Account{ //储蓄卡
    //取钱 (需要重写父类中的抽象方法)

    //存钱(需要重写父类中的抽象方法)
}
class CheckAccount extends Account{ //信用卡
    //取钱(需要重写父类中的抽象方法)

    //存钱(需要重写父类中的抽象方法)
}
//....

2. abstract的概念:抽象的

3. abstract可以用来修饰:类、方法

4. 具体的使用:

4.1 abstract修饰类:
    > 此类称为抽象类
    > 抽象类不能实例化。
    > 抽象类中是包含构造器的,因为子类对象实例化时,需要直接或间接的调用到父类的构造器。
    > 抽象类中可以没有抽象方法。反之,抽象方法所在的类,一定是抽象类。


4.2 abstract修饰方法:
    > 此方法即为抽象方法
    > 抽象方法只有方法的声明,没有方法体。
    > 抽象方法其功能是确定的(通过方法的声明即可确定),只是不知道如何具体实现(体现为没有方法体)
    > 子类必须重写父类中的所有的抽象方法之后,方可实例化。否则,此子类仍然是一个抽象类。


5. abstract不能使用的场景:
5.1 abstract 不能修饰哪些结构?
属性、构造器、代码块等。


5.2 abstract 不能与哪些关键字共用?(自洽)

不能用abstract修饰私有方法、静态方法、final的方法、final的类。
> 私有方法不能重写
> 避免静态方法使用类进行调用
> final的方法不能被重写
> final修饰的类不能有子类

public abstract class Person extends Creature{ //抽象类
    String name;
    int age;

    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public abstract void eat(); //抽象方法

    public abstract void sleep(); //抽象方法
}

public class Student extends Person{
    String school;

    public Student() {
    }

    public Student(String name, int age, String school) {
        super(name, age);
        this.school = school;
    }

    public void eat(){
        System.out.println("学生多吃有营养的食物");
    }

    public void sleep(){
        System.out.println("学生要保证充足的睡眠");
    }

    @Override
    public void breath() {
        System.out.println("学生应该多呼吸新鲜空气");
    }
}

public abstract class Worker extends Person{


    @Override
    public void eat() {
        System.out.println("工人很辛苦,多吃");
    }


}

public abstract class Creature { //生物类
    public abstract void breath();//呼吸
}

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

//        Person p1 = new Person();
//        p1.eat();

        Student s1 = new Student();
        s1.eat();

//        Worker w1 = new Worker();


    }
}

8、模板方法设计模式

public class TemplateTest {
    public static void main(String[] args) {
        PrintPrimeNumber p =  new PrintPrimeNumber();

        p.spendTime();
    }
}

abstract class Template {

    //计算某段代码的执行,需要花费的时间
    public void spendTime() {

        long start = System.currentTimeMillis();

        code();

        long end = System.currentTimeMillis();

        System.out.println("花费的时间为:" + (end - start));
    }

    public abstract void code();

}

class PrintPrimeNumber extends Template {

    @Override
    public void code() {

        for (int i = 2; i <= 100000; i++) {
            boolean isFlag = true;
            for (int j = 2; j <= Math.sqrt(i); j++) {
                if (i % j == 0) {
                    isFlag = false;
                    break;
                }
            }
            if (isFlag) {
                System.out.println(i);
            }
        }

    }
}

案例:

编写工资系统,实现不同类型员工(多态)的按月发放工资。如果当月出现某个Employee对象的生日,则将该雇员的工资增加100元。

实验说明:

(1)定义一个Employee类,该类包含:

private成员变量name,number,birthday,其中birthday 为MyDate类的对象;
提供必要的构造器;
abstract方法earnings(),返回工资数额;
toString()方法输出对象的name,number和birthday。

(2)MyDate类包含:
private成员变量year,month,day;
提供必要的构造器;
toDateString()方法返回日期对应的字符串:xxxx年xx月xx日

(3)定义SalariedEmployee类继承Employee类,实现按月计算工资的员工处理。
该类包括:private成员变量monthlySalary;
提供必要的构造器;
实现父类的抽象方法earnings(),该方法返回monthlySalary值;
toString()方法输出员工类型信息及员工的name,number,birthday。比如:SalariedEmployee[name = '',number = ,birthday=xxxx年xx月xx日]

(4)参照SalariedEmployee类定义HourlyEmployee类,实现按小时计算工资的员工处理。该类包括:
private成员变量wage和hour;
提供必要的构造器;
实现父类的抽象方法earnings(),该方法返回wage*hour值;
toString()方法输出员工类型信息及员工的name,number,birthday。

(5)定义PayrollSystem类,创建Employee变量数组并初始化,该数组存放各类雇员对象的引用。
利用循环结构遍历数组元素,输出各个对象的类型,name,number,birthday,以及该对象生日。
当键盘输入本月月份值时,如果本月是某个Employee对象的生日,还要输出增加工资信息。

//提示:
//定义People类型的数组People c1[]=new People[10];
//数组元素赋值
c1[0]=new People("John","0001",20);
c1[1]=new People("Bob","0002",19);
//若People有两个子类Student和Officer,则数组元素赋值时,可以使父类类型的数组元素指向子类。
c1[0]=new Student("John","0001",20,85.0);
c1[1]=new Officer("Bob","0002",19,90.5);

public abstract class Employee {
    private String name;
    private int number;
    private MyDate birthday;

    public Employee() {
    }

    public Employee(String name, int number, MyDate birthday) {
        this.name = name;
        this.number = number;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

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

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }

    public abstract double earnings();

    public String toString(){
        return "name = " + name + ",number = " + number +
                ", birthday = " + birthday.toDateString();
    }
}
public class HourlyEmployee extends Employee{

    private double wage;//单位小时的工资
    private int hour;//月工作的小时数

    public HourlyEmployee() {
    }

    public HourlyEmployee(String name, int number, MyDate birthday, double wage, int hour) {
        super(name, number, birthday);
        this.wage = wage;
        this.hour = hour;
    }

    public double getWage() {
        return wage;
    }

    public void setWage(double wage) {
        this.wage = wage;
    }

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        this.hour = hour;
    }

    @Override
    public double earnings() {
        return wage * hour;
    }

    public String toString(){
        return "HourlyEmployee[" + super.toString() + "]";
    }
}
public class MyDate {
    private int year;
    private int month;
    private int day;

    public MyDate() {
    }

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public String toDateString(){
        return year + "年" + month + "月" + day + "日";
    }
}

public class SalariedEmployee extends Employee{

    private double monthlySalary;//月工资

    public SalariedEmployee() {
    }

    @Override
    public double earnings() {
        return monthlySalary;
    }

    public SalariedEmployee(String name, int number, MyDate birthday, double monthlySalary) {
        super(name, number, birthday);
        this.monthlySalary = monthlySalary;
    }

//    public double getMonthlySalary() {
//        return monthlySalary;
//    }

    public void setMonthlySalary(double monthlySalary) {
        this.monthlySalary = monthlySalary;
    }

    public String toString(){
        return "SalariedEmployee[" + super.toString() + "]";
    }
}
public class PayrollSystem {
    public static void main(String[] args) {

        Scanner scan = new Scanner(System.in);

        Employee[] emps = new Employee[2];

        emps[0] = new SalariedEmployee("张小亮",1001,
                new MyDate(1992,12,21),18000);

        emps[1] = new HourlyEmployee("侯少鹏",1002,new MyDate(1997,11,12),
                240,100);

        System.out.println("请输入当前的月份:");
        int month = scan.nextInt();

        for (int i = 0; i < emps.length; i++) {
            System.out.println(emps[i].toString());
            System.out.println("工资为:" + emps[i].earnings());

            if(month == emps[i].getBirthday().getMonth()){
                System.out.println("生日快乐!加薪100");
            }
        }

        scan.close();

    }
}

9、接口的使用

接口的使用


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

2. 定义接口的关键字:interface

3. 接口内部结构的说明:
   > 可以声明:
        属性:必须使用public static final修饰
        方法:jdk8之前:声明抽象方法,修饰为public abstract
             jdk8:声明静态方法、默认方法
             jdk9:声明私有方法

   > 不可以声明:构造器、代码块等


4. 接口与类的关系 :实现关系

5. 格式:class A extends SuperA implements B,C{}

A相较于SuperA来讲,叫做子类
A相较于B,C来讲,叫做实现类。


6. 满足此关系之后,说明:
> 类可以实现多个接口。
> 类针对于接口的多实现,一定程度上就弥补了类的单继承的局限性。
> 类必须将实现的接口中的所有的抽象方法都重写(或实现),方可实例化。否则,此实现类必须声明为抽象类。

7. 接口与接口的关系:继承关系,且可以多继承


8. 接口的多态性: 接口名 变量名 = new 实现类对象;


9. 面试题:区分抽象类和接口

> 共性:都可以声明抽象方法
       都不能实例化

> 不同:① 抽象类一定有构造器。接口没有构造器
       ② 类与类之间继承关系,类与接口之间是实现关系,接口与接口之间是多继承关系

public class USBTest {

    public static void main(String[] args) {

        //1.创建接口实现类的对象
        Computer computer =  new Computer();
        Printer printer = new Printer();

        computer.transferData(printer);

        //2.创建接口实现类的匿名对象
        computer.transferData(new Camera());
        System.out.println();

        //3.创建接口匿名实现类的对象
        USB usb1 = new USB(){
            public void start(){
                System.out.println("U盘开始工作");
            }
            public void stop(){
                System.out.println("U盘结束工作");
            }
        };
        computer.transferData(usb1);

        //4. 创建接口匿名实现类的匿名对象

        computer.transferData(new USB(){
            public void start(){
                System.out.println("扫描仪开始工作");
            }
            public void stop(){
                System.out.println("扫描仪结束工作");
            }
        });

    }

}

class Computer{

    public void transferData(USB usb){ //多态:USB usb = new Printer();
        System.out.println("设备连接成功....");
        usb.start();

        System.out.println("数据传输的细节操作....");

        usb.stop();
    }

}

class Camera implements USB{

    @Override
    public void start() {
        System.out.println("照相机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("照相机结束工作");
    }
}

class Printer implements USB{

    @Override
    public void start() {
        System.out.println("打印机开始工作");
    }

    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
}

interface USB{
    //声明常量
    //USB的长、宽、高、...


    //方法
    public abstract void start();
    void stop();
}

10、接口的课后练习

案例1:

1、声明接口Eatable,包含抽象方法public abstract void eat();
2、声明实现类中国人Chinese,重写抽象方法,打印用筷子吃饭
3、声明实现类美国人American,重写抽象方法,打印用刀叉吃饭
4、声明实现类印度人Indian,重写抽象方法,打印用手抓饭
5、声明测试类EatableTest,创建Eatable数组,存储各国人对象,并遍历数组,调用eat()方法

public interface Eatable {
    void eat();
}
public class Chinese implements Eatable{
    @Override
    public void eat() {
        System.out.println("中国人使用筷子吃饭");
    }
}
public class American implements Eatable{
    @Override
    public void eat() {
        System.out.println("美国人使用刀叉吃饭");
    }
}
public class Indian implements Eatable{
    @Override
    public void eat() {
        System.out.println("印度人使用手抓饭");
    }
}
public class EatableTest {
    public static void main(String[] args) {
        Eatable[] eatables = new Eatable[3];

        eatables[0] = new Chinese(); //多态性
        eatables[1] = new American();
        eatables[2] = new Indian();

        for (int i = 0; i < eatables.length; i++) {
            eatables[i].eat();

        }
    }
}

案例2:


定义一个接口用来实现两个对象的比较。

interface CompareObject{
    //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
    public int compareTo(Object o);
}


定义一个Circle类,声明radius属性,提供getter和setter方法

定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。
在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小。

定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小。

拓展:参照上述做法定义矩形类Rectangle和ComparableRectangle类,在ComparableRectangle类
中给出compareTo方法的实现,比较两个矩形的面积大小。

public class Circle {
    private double radius;//半径

    public Circle() {
    }

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

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Circle{" +
                "radius=" + radius +
                '}';
    }
}
public class ComparableCircle extends Circle implements CompareObject{
    public ComparableCircle() {
    }

    public ComparableCircle(double radius) {
        super(radius);
    }

    //根据对象的半径的大小,比较对象的大小
    @Override
    public int compareTo(Object o) {
        if(this == o){
            return 0;
        }

        if(o instanceof ComparableCircle){
            ComparableCircle c = (ComparableCircle)o;
            //错误的
//            return (int) (this.getRadius() - c.getRadius());
            //正确的写法1:
//            if(this.getRadius() > c.getRadius()){
//                return 1;
//            }else if(this.getRadius() < c.getRadius()){
//                return -1;
//            }else{
//                return 0;
//            }
            //正确的写法2:
            return Double.compare(this.getRadius(),c.getRadius());
        }else{
            return 2; //如果输入的类型不匹配,则返回2
//            throw new RuntimeException("输入的类型不匹配");
        }

    }
}
public interface CompareObject {
    //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
    public int compareTo(Object o);
}
public class InterfaceTest {
    public static void main(String[] args) {

        ComparableCircle c1 = new ComparableCircle(2.3);
        ComparableCircle c2 = new ComparableCircle(5.3);

        int compareValue = c1.compareTo(c2);
        if(compareValue > 0){
            System.out.println("c1对象大");
        }else if(compareValue < 0){
            System.out.println("c2对象大");
        }else{
            System.out.println("c1和c2一样大");
        }
    }
}

案例3:
阿里的一个工程师Developer,结构见图。

其中,有一个乘坐交通工具的方法takingVehicle(),在此方法中调用交通工具的run()。
为了出行方便,他买了一辆捷安特自行车、一辆雅迪电动车和一辆奔驰轿车。这里涉及到的相关类及接口关系如图。

其中,电动车增加动力的方式是充电,轿车增加动力的方式是加油。在具体交通工具的run()中调用其所在类
的相关属性信息。

请编写相关代码,并测试。

提示:创建Vehicle[]数组,保存阿里工程师的三辆交通工具,并分别在工程师的takingVehicle()中调用。

public class Bicycle extends Vehicle{

    public Bicycle() {
    }

    public Bicycle(String brand, String color) {
        super(brand, color);
    }

    @Override
    public void run() {
        System.out.println("自行车通过人力脚蹬行驶");
    }
}
public class Car extends Vehicle implements IPower{

    private String carNumber;

    public Car() {
    }

    public Car(String brand, String color, String carNumber) {
        super(brand, color);
        this.carNumber = carNumber;
    }

    public String getCarNumber() {
        return carNumber;
    }

    public void setCarNumber(String carNumber) {
        this.carNumber = carNumber;
    }

    @Override
    public void run() {
        System.out.println("汽车通过内燃机驱动行驶");
    }

    @Override
    public void power() {
        System.out.println("汽车通过汽油提供动力");
    }
}
public class Developer {
    private String name;
    private int age;

    public Developer() {
    }

    public Developer(String name, int age) {
        this.name = name;
        this.age = age;
    }

    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;
    }

    public void takingVehicle(Vehicle vehicle){
        vehicle.run();
    }
}
public class ElectricVehicle extends Vehicle implements IPower{


    public ElectricVehicle() {
    }

    public ElectricVehicle(String brand, String color) {
        super(brand, color);
    }

    @Override
    public void run() {
        System.out.println("电动车通过电机驱动行驶");
    }
    @Override
    public void power() {
        System.out.println("电动车使用电力提供动力");
    }
}
public interface IPower {
    void power();
}
public abstract class Vehicle {
    private String brand;//品牌
    private String color;//颜色

    public Vehicle() {
    }

    public Vehicle(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public abstract void run();
}
public class VehicleTest {
    public static void main(String[] args) {

        Developer developer = new Developer();

        //创建三个交通工具,保存在数组中
        Vehicle[] vehicles = new Vehicle[3];
        vehicles[0] = new Bicycle("捷安特","骚红色");
        vehicles[1] = new ElectricVehicle("雅迪","天蓝色");
        vehicles[2] = new Car("奔驰","黑色","沪Au888");

        for (int i = 0;i < vehicles.length;i++){
            developer.takingVehicle(vehicles[i]);

            if(vehicles[i] instanceof IPower){
                ((IPower) vehicles[i]).power();
            }
        }





    }
}

10、内部类


类的成员之五:内部类

1. 什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A就称为`内部类(InnerClass)`,类B则称为`外部类(OuterClass)`。

2. 为什么需要内部类?
具体来说,当一个事物A的内部,还有一个部分需要一个完整的结构B进行描述,而这个内部的完整的结构B又只为外部事物A
提供服务,不在其他地方单独使用,那么整个内部的完整结构B最好使用内部类。

总的来说,遵循`高内聚、低耦合`的面向对象开发原则。


3. 内部类使用举例:
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的key和value

4. 内部类的分类:(参考变量的分类)
    > 成员内部类:直接声明在外部类的里面。
        > 使用static修饰的:静态的成员内部类
        > 不使用static修饰的:非静态的成员内部类

    > 局部内部类:声明在方法内、构造器内、代码块内的内部类
        > 匿名的局部内部类
        > 非匿名的局部内部类


5. 内部类这节要讲的知识:
  > 成员内部类的理解
  > 如何创建成员内部类的实例
  > 如何在成员内部类中调用外部类的结构
  > 局部内部类的基本使用


6. 关于成员内部类的理解:
   > 从类的角度看:
        - 内部可以声明属性、方法、构造器、代码块、内部类等结构
        - 此内部类可以声明父类,可以实现接口
        - 可以使用final修饰
        - 可以使用abstract修饰

   > 从外部类的成员的角度看:
        - 在内部可以调用外部类的结构。比如:属性、方法等
        - 除了使用public、缺省权限修饰之外,还可以使用private、protected修饰
        - 可以使用static修饰

案例1:

编写一个匿名内部类,它继承Object,并在匿名内部类中,声明一个方法public void test()打印尚硅谷。

请编写代码调用这个方法。

public class ObjectTest {
    public static void main(String[] args) {
//        SubObject sub1 = new SubObject();
//        sub1.test();


        //提供有一个继承于Object的匿名子类的匿名对象
        new Object(){
            public void test(){
                System.out.println("尚硅谷");
            }
        }.test();


    }
}

class SubObject extends Object{
    public void test(){
        System.out.println("尚硅谷");
    }
}

11、枚举的两种定义方式


枚举类的使用

1. 枚举类的理解:
枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。

2. 举例:
- `星期`:Monday(星期一)......Sunday(星期天)
- `性别`:Man(男)、Woman(女)
- `月份`:January(1月)......December(12月)
- `季节`:Spring(春节)......Winter(冬天)
- `三原色`:red(红色)、green(绿色)、blue(蓝色)
- `支付方式`:Cash(现金)、WeChatPay(微信)、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)
- `就职状态`:Busy(忙碌)、Free(空闲)、Vocation(休假)、Dimission(离职)
- `订单状态`:Nonpayment(未付款)、Paid(已付款)、Fulfilled(已配货)、Delivered(已发货)、Checked(已确认收货)、Return(退货)、Exchange(换货)、Cancel(取消)
- `线程状态`:创建、就绪、运行、阻塞、死亡

3. 开发中的建议:
> 开发中,如果针对于某个类,其实例是确定个数的。则推荐将此类声明为枚举类。
> 如果枚举类的实例只有一个,则可以看做是单例的实现方式。


4. JDK5.0 之前如何自定义枚举类 (了解)
见代码

public class SeasonTest {
    public static void main(String[] args) {
//        Season.SPRING = null;

        System.out.println(Season.SPRING);

        System.out.println(Season.SUMMER.getSeasonName());
        System.out.println(Season.SUMMER.getSeasonDesc());
    }
}

//jdk5.0之前定义枚举类的方式
class Season{
    //2. 声明当前类的对象的实例变量,使用private final修饰
    private final String seasonName;//季节的名称
    private final String seasonDesc;//季节的描述

    //1. 私有化类的构造器
    private Season(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //3. 提供实例变量的get方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }
    //4. 创建当前类的实例,需要使用public static final修饰
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","夏日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高气爽");
    public static final Season WINTER = new Season("冬天","白雪皑皑");

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }
}

5. JDK5.0中使用enum定义枚举类
见代码

public class SeasonTest1 {
    public static void main(String[] args) {
        System.out.println(Season1.SPRING.getClass());
        System.out.println(Season1.SPRING.getClass().getSuperclass());
        System.out.println(Season1.SPRING.getClass().getSuperclass().getSuperclass());

    }
}

enum Season1 {

    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "下日燕燕"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "白雪皑皑");

    private final String seasonName;
    private final String seasonDesc;


    private Season1(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }


}

6. Enum中的常用方法:
6.1 使用enum关键字定义的枚举类,默认其父类是java.lang.Enum类
    使用enum关键字定义的枚举类,不要再显示的定义其父类。否则报错。
6.2 熟悉Enum类中常用的方法
    String toString(): 默认返回的是常量名(对象名),可以继续手动重写该方法!
    (关注)static 枚举类型[] values():返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法
    (关注)static 枚举类型 valueOf(String name):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException。
    String name():得到当前枚举常量的名称。建议优先使用toString()。
    int ordinal():返回当前枚举常量的次序号,默认从0开始

7. 枚举类实现接口的操作
   情况1:枚举类实现接口,在枚举类中重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是同一个方法。
   情况2:让枚举类的每一个对象重写接口中的抽象方法。当通过不同的枚举类对象调用此方法时,执行的是不同的实现的方法。

public class SeasonTest1 {
    public static void main(String[] args) {
//        System.out.println(Season1.SPRING.getClass());
//        System.out.println(Season1.SPRING.getClass().getSuperclass());
//        System.out.println(Season1.SPRING.getClass().getSuperclass().getSuperclass());

        //测试方法
        //1. toString()
        System.out.println(Season1.SPRING);
        System.out.println(Season1.AUTUMN);

        //2. name()
        System.out.println(Season1.SPRING.name());

        //3. vlaues()
        Season1[] values = Season1.values();
        for (int i = 0; i < values.length; i++) {
            System.out.println(values[i]);
        }

        //4. valueOf(String objName):返回当前枚举类中名称为objName的枚举类对象。
        //如果枚举类中不存在objName名称的对象,则报错。
        String objName = "WINTER";
//        objName = "WINTER1";
        Season1 season1 = Season1.valueOf(objName);
        System.out.println(season1);

        //5.ordinal()
        System.out.println(Season1.AUTUMN.ordinal());

        //通过枚举类的对象调用重写接口中的方法
        Season1.SUMMER.show();
    }
}

interface Info{
    void show();
}

//jdk5.0中使用enum关键字定义枚举类
enum Season1 implements Info{
    //1. 必须在枚举类的开头声明多个对象。对象之间使用,隔开
    SPRING("春天","春暖花开"),
    SUMMER("夏天","夏日炎炎"),
    AUTUMN("秋天","秋高气爽"),
    WINTER("冬天","白雪皑皑");

    //2. 声明当前类的对象的实例变量,使用private final修饰
    private final String seasonName;//季节的名称
    private final String seasonDesc;//季节的描述

    //3. 私有化类的构造器
    private Season1(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //4. 提供实例变量的get方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season1{" +
                "seasonName='" + seasonName + '\'' +
                ", seasonDesc='" + seasonDesc + '\'' +
                '}';
    }

    @Override
    public void show() {
        System.out.println("这是一个季节");
    }
}

案例1:

案例:使用枚举类实现单例模式

public class BankTest1 {
    public static void main(String[] args) {
//        Bank1.instance = null;

        System.out.println(GirlFriend.XIAO_LI);
    }
}

//jdk5.0之前的使用枚举类定义单例模式
class Bank1{

    private Bank1(){}

    public static final Bank1 instance = new Bank1();


}
//jdk5.0使用enum关键字定义枚举类的方式定义单例模式
enum Bank2{
    CPB;
}

enum GirlFriend{

    XIAO_LI(20);

    private final int age;

    private GirlFriend(int age){
        this.age = age;
    }

}

案例2:

案例:颜色枚举类Color(使用enum声明)

1、声明颜色枚举类:7个常量对象:RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE;

2、在测试类中,使用枚举类,获取绿色对象,并打印对象。

public enum Color {
    RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE;
}

案例3:
案例拓展:颜色枚举类(使用enum声明)
(1)声明颜色枚举类Color:

- 声明final修饰的int类型的属性red,green,blue
- 声明final修饰的String类型的属性description
- 声明有参构造器Color(int red, int green, int blue,String description)
- 创建7个常量对象:红、橙、黄、绿、青、蓝、紫,
- 重写toString方法,例如:RED(255,0,0)->红色

(2)在测试类中,使用枚举类,获取绿色对象,并打印对象。

提示:
- 7个常量对象的RGB值如下:
红:(255,0,0)
橙:(255,128,0)
黄:(255,255,0)
绿:(0,255,0)
青:(0,255,255)
蓝:(0,0,255)
紫:(128,0,255)

7个常量对象名如下:
RED, ORANGE, YELLOW, GREEN, CYAN, BLUE,PURPLE

public class ColorTest {
    public static void main(String[] args) {
        System.out.println(Color.GREEN);
    }
}

enum Color{
    RED(255,0,0,"红色"),
    ORANGE(255,128,0,"橙色"),
    YELLOW(255,255,0,"黄色"),
    GREEN(0,255,0,"绿色"),
    CYAN(0,255,255,"青色"),
    BLUE(0,0,255,"蓝色"),
    PURPLE(128,0,255,"紫色");

    private final int red;
    private final int green;
    private final int blue;
    private final String description;//颜色的描述

    Color(int red, int green, int blue, String description) {
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.description = description;
    }

    public int getRed() {
        return red;
    }

    public int getGreen() {
        return green;
    }

    public int getBlue() {
        return blue;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
//        return name() + "(" + red + "," + green + "," + blue + ")->" + description;
        return super.toString() + "(" + red + "," + green + "," + blue + ")->" + description;
    }
}

12、Annotation的注解、单元测试

注解的使用

1. Annotation的理解
> 注解(Annotation)是从`JDK5.0`开始引入,以“`@注解名`”在代码中存在。
> Annotation 可以像修饰符一样被使用,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明。
  还可以添加一些参数值,这些信息被保存在 Annotation 的 “name=value” 对中。
> 注解可以在类编译、运行时进行加载,体现不同的功能。

2. 注解的应用场景:
示例1:生成文档相关的注解
示例2:在编译时进行格式检查(JDK内置的三个基本注解)
示例3:跟踪代码依赖性,实现替代配置文件功能


3. Java基础涉及到的三个常用注解
`@Override`: 限定重写父类方法,该注解只能用于方法
`@Deprecated`: 用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择
`@SuppressWarnings`: 抑制编译器警告

public class AnnotationTest {
    public static void main(String[] args) {
        Person p1 = new Student();
        p1.walk();

        Date date = new Date();
        System.out.println(date);

        Date date1 = new Date(2022,11,29);
        System.out.println(date1);

        Person p2 = new Person();
        @SuppressWarnings({"RedundantExplicitVariableType"}) Person p3 = new Person("Tom");
        System.out.println(p3);


        @SuppressWarnings("unused") int num = 10;

    }
}

@MyAnnotation(value="class")
class Person{
    String name;
    int age;

    @MyAnnotation()
    public Person(){}

    @Deprecated
    public Person(String name){
        this.name = name;
    }

    public void eat(){
        System.out.println("人吃饭");
    }

    public void walk(){
        System.out.println("人走路");
    }

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

class Student extends Person{

    @Override
    public void eat(){
        System.out.println("学生吃饭");
    }
    @Override
    public void walk(){
        System.out.println("学生走路");
    }
}

4. 自定义注解
以@SuppressWarnings为参照,进行定义即可。

5. 元注解的理解:
元注解:对现有的注解进行解释说明的注解。

讲4个元注解:
(1)@Target:用于描述注解的使用范围
可以通过枚举类型ElementType的10个常量对象来指定
TYPE,METHOD,CONSTRUCTOR,PACKAGE.....

(2)@Retention:用于描述注解的生命周期
可以通过枚举类型RetentionPolicy的3个常量对象来指定
SOURCE(源代码)、CLASS(字节码)、RUNTIME(运行时)
唯有RUNTIME阶段才能被反射读取到。

(3)@Documented:表明这个注解应该被 javadoc工具记录。
(4)@Inherited:允许子类继承父类中的注解


拓展: 元数据。
String name = "Tom";


框架 = 注解 + 反射 + 设计模式
 

12、包装类的理解


包装类的使用

1. 为什么要使用包装类?
为了使得基本数据类型的变量具备引用数据类型变量的相关特征(比如:封装性、继承性、多态性),我们给各个基本数据
类型的变量都提供了对应的包装类。


2. (掌握)基本数据类型对应的包装类类型
byte -> Byte
short -> Short
int -> Integer
long -> Long
float -> Float
double ->Double

char -> Character
boolean -> Boolean


3. 掌握基本数据类型 与 包装类之间的转换。
   3.1 为什么需要转换
     > 一方面,在有些场景下,需要使用基本数据类型对应的包装类的对象。此时就需要将基本数据类型的变量转换为
       包装类的对象。比如:ArrayList的add(Object obj);Object类的equals(Object obj)
     > 对于包装类来讲,既然我们使用的是对象,那么对象是不能进行+ - * /等运算的。为了能够进行这些运算,就
       需要将包装类的对象转换为基本数据类型的变量。

   3.2 如何转换:
        (装箱)基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
        (拆箱)包装类 ---> 基本数据类型:调用包装类的xxxValue()

    注意:原来使用基本数据类型变量的位置,改成包装类以后,对于成员变量来说,其默认值变化了!

    jdk5.0新特性:自动装箱、自动拆箱。

public class WrapperTest {

    /*
    基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
    包装类 ---> 基本数据类型:调用包装类的xxxValue()
     */
    //包装类 ---> 基本数据类型:调用包装类的xxxValue()
    /*
    * jdk5.0新特性:自动装箱、自动拆箱。
    * */
    @Test
    public void test4(){
        //自动装箱:   基本数据类型 ---> 包装类
        int i1 = 10;
        Integer ii1 = i1; //自动装箱
        System.out.println(ii1.toString());

        Integer ii2 = i1 + 1; //自动装箱

        Boolean bb1 = true;//自动装箱

        Float f1 = 12.3F;  //自动装箱

        //自动拆箱:包装类 ---> 基本数据类型

        int i2 = ii1;  //自动拆箱

        boolean b1 = bb1;//自动拆箱



    }
    @Test
    public void test3(){
        Account account = new Account();
        System.out.println(account.isFlag1);//false
        System.out.println(account.isFlag2);//null

        System.out.println(account.balance1);//0.0
        System.out.println(account.balance2);//null
    }

    @Test
    public void test2(){
        Integer ii1 = new Integer(10);
        int i1 = ii1.intValue();
        i1 = i1 + 1;

        Float ff1 = new Float(12.3F);
        float f1 = ff1.floatValue();


        Boolean bb1 = Boolean.valueOf(true);
        boolean b1 = bb1.booleanValue();


    }

    //基本数据类型 ---> 包装类:① 使用包装类的构造器 ② (建议)调用包装类的valueOf(xxx xx)
    @Test
    public void test1(){
        int i1 = 10;
        Integer ii1 = new Integer(i1);
        System.out.println(ii1.toString());

        float f1 = 12.3F;
        f1 = 32.2f;
        Float ff1 = new Float(f1);
        System.out.println(ff1.toString());

        String s1 = "32.1";
        Float ff2 = new Float(s1);

//        s1 = "abc";
//        Float ff3 = new Float(s1); //报异常:NumberFormatException


        boolean b1 = true;
        Boolean bb1 = new Boolean(b1);
        System.out.println(bb1);

        String s2 = "false";
        s2 = "FaLse123";
        s2 = "TrUe";
        Boolean bb2 = new Boolean(s2);
        System.out.println(bb2); //false  --> true

        //推荐使用:
        int i2 = 10;
        Integer ii2 = Integer.valueOf(i2);

        Boolean b2 = Boolean.valueOf(true);

        Float f2 = Float.valueOf(12.3F);


    }

}

class Account{
    boolean isFlag1;
    Boolean isFlag2;

    double balance1; //0.0
    Double balance2; //null  0.0
}

4. String 与 基本数据类型、包装类之间的转换。

   基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx) ; ② 基本数据类型的变量 + ""

   String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()

public class WrapperTest1 {
    /*
    基本数据类型、包装类 ---> String类型:① 调用String的重载的静态方法valueOf(xxx xx) ; ② 基本数据类型的变量 + ""

    String类型 ---> 基本数据类型、包装类: 调用包装类的静态方法:parseXxx()
     */
    @Test
    public void test2(){
        String s1 = "123";
        int i1 = Integer.parseInt(s1);
        System.out.println(i1 + 10);


        String s2 = "true";
        boolean b1 = Boolean.parseBoolean(s2);


        //特别的
//        String s3 = "123a";
//        int i2 = Integer.parseInt(s3); //报错:NumberFormatException

    }
    @Test
    public void test1(){
        //方式1:调用String的重载的静态方法valueOf(xxx xx)
        int i1 = 10;
        String str1 = String.valueOf(i1);
        System.out.println(str1);//"10"

        boolean b1 = true;
        Boolean b2 = b1;
        String str2 = String.valueOf(b1);
        String str3 = String.valueOf(b2);

        //方式2:基本数据类型的变量 + ""
        String str4 = i1 + "";
        String str5 = b1 + "";
    }
}

案例1:

利用Vector代替数组处理:从键盘读入学生成绩(以负数代表输入结束),找出最高分,并输出学生成绩等级。

提示:数组一旦创建,长度就固定不变,所以在创建数组前就需要知道它的长度。而向量类java.util.Vector可以根据需要动态伸缩。

1、创建Vector对象:Vector v=new Vector();

2、给向量添加元素:v.addElement(Object obj);   //obj必须是对象

3、取出向量中的元素:Object  obj = v.elementAt(0);
   注意第一个元素的下标是0,返回值是Object类型的。

4、计算向量的长度:v.size();

5、若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等

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

        //1. 创建Vector对象:Vector v=new Vector();
        Vector v = new Vector();

        Scanner scanner = new Scanner(System.in);

        int maxScore = 0; //记录最高分



        //2. 从键盘获取多个学生成绩,存放到v中 (以负数代表输入结束)
        while(true){ //for(;;)

            System.out.print("请输入学生成绩(以负数代表输入结束):");
            int intScore = scanner.nextInt();

            if(intScore < 0){
                break;
            }

//            //装箱:int --> Integer对象
//            Integer score = Integer.valueOf(intScore);
//            //添加学生成绩到容器v中
//            v.addElement(score);

            //jdk5.0之后:自动装箱
            v.addElement(intScore);

            //3. 获取学生成绩的最大值
            if(maxScore < intScore){
                maxScore = intScore;
            }
        }

        System.out.println("最高分:" + maxScore);
        //4. 依次获取v中的每个学生成绩,与最高分进行比较,获取学生等级,并输出
        for(int i = 0;i < v.size();i++){
            Object objScore = v.elementAt(i);
            //方式1:
//            Integer integerScore = (Integer) objScore;
//            //拆箱
//            int score = integerScore.intValue();

            //方式2:自动拆箱
            int score = (Integer) objScore;
            char grade;
            if(maxScore - score <= 10){
                grade = 'A';
            }else if(maxScore - score <= 20){
                grade = 'B';
            }else if(maxScore - score <= 30){
                grade = 'C';
            }else{
                grade = 'D';
            }

            System.out.println("student " + i +" score is " + score + " grade is " + grade);

        }

        scanner.close();
    }
}


 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值