第六章----面向对象(下)

一、static关键字

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

在这里插入图片描述
|-static关键字的使用

①可以用来修饰:属性、方法、代码块、内部类
②使用static修饰属性:静态变量(或类变量)
    属性,按是否使用static修饰,又分为:静态属性 VS 非静态属性(实例变量)
    实例变量:创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改
    静态变量:创建了类的多个对象,多个对象共享一个静态变量,当通过某一个静态对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

static修饰属性的其他说明:

①静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用
②静态变量的加载要早于对象的创建
③由于类只会加载一次,则静态变量在内存中也只会存在一份

在这里插入图片描述例:

package com.atguigu.java;
public class StsticTest {
    public static void main(String[] args) {
         Chinese.nation = "中国";
         Chinese c1 = new Chinese();
         c1.name = "姚明";
         c1.age = 40;
         c1.nation = "CHN";
         Chinese c2 = new Chinese();
         c2.name = "马龙";
         c2.age = 30;
         c2.nation = "CHINA";
         System.out.println(c2.nation);

           // 编译不通过
        // Chinese.name = "张继科";
    }
}
class Chinese {
    String name;
    int age;
    static String nation;
}

|-类变量vs 实例变量内存解析:
在这里插入图片描述|-使用static修饰方法:静态方法

①随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
②静态方法中,只能调用静态的方法或属性
 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
③在静态方法内,不能使用this关键字,super关键字
④静态属性和静态方法的使用,从生命周期的角度理解

在这里插入图片描述例:

package com.atguigu.java;
import java.util.jar.Attributes.Name;
public class StsticTest {
    public static void main(String[] args) {
         Chinese.nation = "中国";
         Chinese c1 = new Chinese();
         c1.name = "姚明";
         c1.age = 40;
         c1.nation = "CHN";
         Chinese c2 = new Chinese();
         c2.name = "马龙";
         c2.age = 30;
         c2.nation = "CHINA";
         System.out.println(c2.nation);
         // 编译不通过
         // Chinese.name = "张继科";
         c1.eat();
         Chinese.show();
         // 编译不通过
         // Chinese.eat();
//       Chinese.info();
    }
}
class Chinese {
    String name;
    int age;
    static String nation;
    public void eat() {
         System.out.println("中国人吃中餐!");
         // 调用非静态结构
         this.info();
         System.out.println("name" + name);
         // 调用静态结构
         walk();
         System.out.println("nation" +  this.nation);
    }
    public static void show() {
         System.out.println("我是一个中国人!");
         // 不能调用非静态的结构
//       eat();
         // name = "Tom";
         // 可以调用静态的结构
         System.out.println(Chinese.nation);
         walk();
    }
    public void info() {
         System.out.println("name" + name + ",age"  + age);
    }
    public static void walk() {
    }
}

|-属性和方法是否应该static
属性:

①属性是可以被多个对象所共享的,不会随着对象的不同而不同的
②类中的常量也常常声明为static

方法:

①操作静态属性的方法,通常设置为static的
②工具类中的方法,习惯上声明为static的,如:Math、Arrays、Collections

优化Arrayutil:

package com.atguigu.java;
public class ArrayUtil {
    // 求数组的最大值
    public static int getMax(int[] arr) {
         int maxValue = arr[0];
         for (int i = 0; i < arr.length; i++) {
             if (maxValue < arr[i]) {
                 maxValue = arr[i];
             }
         }
         return maxValue;
    }
    // 求数组的最小值
    public static int getMin(int[] arr) {
         int minValue = arr[0];
         for (int i = 0; i < arr.length; i++) {
             if (minValue > arr[i]) {
                 minValue = arr[i];
             }
         }
         return minValue;
    }
    // 求数组的总和
    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 reverse(int[] arr) {
         for (int i = 0; i < arr.length / 2; i++) {
             int temp = arr[i];
             arr[i] = arr[arr.length - i - 1];
             arr[arr.length - i - 1] = temp;
         }
    }
    // 复制数组
    public static int[] copy(int[] arr) {
         int[] arr1 = new int[arr.length];
         for (int i = 0; i < arr1.length; i++) {
             arr1[i] = arr[i];
         }
         return arr1;
    }
    // 数组排序
    public static void sort(int[] arr) {
         for (int i = 0; i < arr.length - 1; i++) {
             for (int j = 0; j < arr.length - 1 -  i; j++) {
                 if (arr[j] > arr[j + 1]) {
//                   int temp = arr[j];
//                   arr[j] = arr[j + 1];
//                   arr[j + 1] = temp;
                     // swap(arr[j], arr[j + 1]);
                     swap(arr, j, j + 1);
                 }
             }
         }
         for (int i = 0; i < arr.length; i++) {
             System.out.print(arr[i] + "\t");
         }
    }
    // 交换数组中指定两个位置元素的值
    public static void swap(int[] arr, int i, int  j) {
         int temp = arr[i];
         arr[i] = arr[j];
         arr[j] = temp;
    }
    // 遍历数组
    public static void print(int[] arr) {
         for (int i = 0; i < arr.length; i++) {
             System.out.print(arr[i] + " \t");
         }
         System.out.println();
    }
    // 查找指定元素
    public static int getIndex(int[] arr, int dest)  {
         for (int i = 0; i < arr.length; i++) {
             if (dest == (arr[i])) {
                 return i;
             }
         }
         return -1;// 返回一个复数,表示没找到
    }
}
package com.atguigu.java;
public class ArrayUtilTest {
    public static void main(String[] args) {
         // ArrayUtil util = new ArrayUtil();
         int[] arr = new int[] { 2, 5, 6, 8, 10,  12, 13, 6 };
         int max = ArrayUtil.getMax(arr);
         System.out.println("最大值为:" + max);
         System.out.println("排序前:");
         ArrayUtil.print(arr);
         System.out.println();
         System.out.println("排序后:");
         ArrayUtil.sort(arr);
         System.out.println();
         System.out.println("查找:");
         int index = ArrayUtil.getIndex(arr, 6);
         if (index > 0) {
             System.out.println("找到了,索引地址为:" + index);
         } else {
             System.out.println("未找到!");
         }
    }
}

|-练习:

例一:

package com.atguigu.java;
public class CircleTest {
    public static void main(String[] args) {
         Circle c1 = new Circle();
         Circle c2 = new Circle();
         Circle c3 = new Circle(3.4);
         System.out.println("c1的ID:" +  c1.getId());
         System.out.println("c1的ID:" +  c2.getId());
         System.out.println("c3的ID:" +  c3.getId());
         System.out.println("创建的圆的个数为:" +  Circle.getTotal());
    }
}
class Circle {
    private double raidus;
    private int id;// 自动赋值
    public Circle() {
         id = init++;
         total++;
    }
    public Circle(double radius) {
         this();
//       id = init++;
//       total++;
         this.raidus = radius;
    }
    private static int total;// 记录创建的圆的个数
    private static int init = 1001;// stataic声明的属性被所有对象所共享
    public double findArea() {
         return 3.14 * raidus * raidus;
    }
    public double getRaidus() {
         return raidus;
    }
    public void setRaidus(double raidus) {
         this.raidus = raidus;
    }
    public int getId() {
         return id;
    }
    public static int getTotal() {
         return total;
    }
}

例二:

package com.atguigu.exer;
public class Account {
    private int id;
    private String pwd = "000000";
    private double balance;
    private static double interestRate;
    private static double minMoney = 1.0;
    private static int init = 1001;// 用于自动生成id使用的
    public Account() {
         id = init++;
    }
    public Account(String pwd, double balance) {
         id = init++;
         this.pwd = pwd;
         this.balance = balance;
    }
    public String getPwd() {
         return pwd;
    }
    public void setPwd(String pwd) {
         this.pwd = pwd;
    }
    public static double getInterestRate() {
         return interestRate;
    }
    public static void setInterestRate(double  interestRate) {
         Account.interestRate = interestRate;
    }
    public static double getMinMoney() {
         return minMoney;
    }
    public static void setMinMoney(double minMoney)  {
         Account.minMoney = minMoney;
    }
    public int getId() {
         return id;
    }
    public double getBalance() {
         return balance;
    }
    @Override
    public String toString() {
         return "Account [id=" + id + ", pwd=" +  pwd + ", balance=" + balance + "]";
    }
}
package com.atguigu.exer;
public class AccountTest {
    public static void main(String[] args) {
         Account acct1 = new Account();
         Account acct2 = new Account("qwerty",  2000);
         Account.setInterestRate(0.012);
         Account.setMinMoney(100);
         System.out.println(acct1);
         System.out.println(acct2);
         System.out.println(acct1.getInterestRate());
         System.out.println(acct1.getMinMoney());
    }
}

|-单例(Singleton)设计模式

设计模式:

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

单例设计模式:

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

①单例的饿汉式实现:

package com.atguigu.java2;
public class Singleton {
    public static void main(String[] args) {
         Bank bank1 = Bank.getInstance();
         Bank bank2 = Bank.getInstance();
         System.out.println(bank1 == bank2);
    }
}
class Bank {
    // 1.私有化类的构造器
    private Bank() {
    }
    // 2.内部创建类的对象
    // 4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();
    // 3.提供公共方法,返回类的对象
    public static Bank getInstance() {
         return instance;
    }
}

②单例的懒汉式实现:

package com.atguigu.java2;
public class SingltonTest2 {
    public static void main(String[] args) {
         Order order1 = Order.getInstance();
         Order order2 = Order.getInstance();
         System.out.println(order1 == order2);
    }
}
class Order {
    // 1.私有化类的构造器
    private Order() {
    }
    // 2.声明当前类对象,没有初始化
    // 4.此对象也必须声明为static的
    private static Order instance = null;
    // 3.声明public、static的返回当前类对象的方法
    public static Order getInstance() {
         if (instance == null) {
             instance = new Order();
         }
         return instance;
    }
}

③区分饿汉式和懒汉式:
饿汉式:

|-坏处:对象加载时间过长
|-好处:线程安全的

懒汉式:

|-好处:延迟对象的创建
|-目前写法坏处:线程不安全

④单例模式的应用场景:

|-网站的计数器,一般也是单例模式实现,否则难以同步。
|-应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
|-数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
|-Application 也是单例的典型应用
|-Windows的Task Manager (任务管理器)就是很典型的单例模式
|-Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

二、理解main()方法的语法
说明:

①main()方法作为程序的入口
②main()方法也是一个普通的静态方法

例一:

package com.atguigu.java2;
public class MainTest {
    public static void main(String[] args) {// 入口
         Main.main(new String[100]);
         MainTest test = new MainTest();
         test.show();
    }
    public void show() {
    }
}
class Main {
    public static void main(String[] args) {
         for (int i = 0; i < args.length; i++) {
             args[i] = "args_" + i;
             System.out.println(args[i]);
         }
    }
}

③main()方法可以作为与控制台交互的方式(如:使用Scanner)

package com.atguigu.java2;
public class MianDemo {
    public static void main(String[] args) {
         for (int i = 0; i < args.length; i++) {
             System.out.println("******" +  args[i]);
             int num = Integer.parseInt(args[i]);
             System.out.println("###" + num);
         }
    }
}

eclipse控制台:(右键–Run As–Run Configurations)
在这里插入图片描述cmd窗口:
在这里插入图片描述三、代码块(初始化块)
①作用

|-用来初始化类、对象
|-代码块如果有修饰的话,只能使用static
|-分类:静态代码块、非静态代码块

在这里插入图片描述例:

package com.atguigu.java2;
import java.util.jar.Attributes.Name;
public class BlockTest {
    public static void main(String[] args) {
         String desc = Person.desc;
         System.out.println(desc);
         Person p1 = new Person();
         Person p2 = new Person();
         System.out.println(p1.age);
         Person.info();
    }
}
class Person {
    // 属性
    String name;
    int age;
    static String desc = "我是一个人!";
    // 构造器
    public Person() {
    }
    public Person(String name, int age) {
         this.name = name;
         this.age = age;
    }
    // static代码块
    static {
         System.out.println("Hello ,static  block-1");、
         //调用静态结构
         desc = "我是一个爱学习的人1!";
         info();
         //不可以调用非静态结构
         //eat();
         //Name = "Tom";
    }
    static {
         System.out.println("Hello ,static  block-2");
    }
    // 非static代码块
    {
         System.out.println("Hello , block");
         // 调用非静态结构
         age = 1;
         desc = "我是一个爱学习的人2!";
         // 调用静态结构
         info();
         eat();
    }
    // 方法
    public void eat() {
         System.out.println("人吃饭!");
    }
    @Override
    public String toString() {
         return "Person [name=" + name + ", age=" +  age + "]";
    }
    public static void info() {
         System.out.println("我是一个快乐的人!");
    }
}

|-代码块的使用情境:
例:

package com.atguigu.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;


import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.apache.commons.dbutils.DbUtils;

import com.atguigu.connection.DBCPTest;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class JDBCUtils {
    
    private static DataSource dataSource = null;
    static{
        InputStream is = null;
        try {
            is = DBCPTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
            Properties pros = new Properties();
            pros.load(is);
            //调用BasicDataSourceFactory的静态方法,获取数据源。
            dataSource = BasicDataSourceFactory.createDataSource(pros);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
            }
            
        }
        
    }
    //使用DBCP数据库连接池实现数据库的连接
    public static Connection getConnection2() throws SQLException{
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
        return conn;
    }
    
}

|-练习:

例一:

package com.atguigu.java3;
//由父及子,静态先行
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();
    }
}

总结:由父及子,静态先行
例二:

package com.atguigu.java3;
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("************************");
         new Son();
         System.out.println("************************");
         new Son();
         System.out.println("************************");
         new Father();
    }
}

|-属性赋值的先后顺序
例:

package com.atguigu.java3;
/*
* 对属性可以赋值的位置:
* ①默认初始化
* ②显式初始化/⑤在代码块中赋值
* ③构造器中初始化
* ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
*
*
  * 执行的先后顺序:① - ② / ⑤ - ③ - ④
*/
public class OrderTest {
    public static void main(String[] args) {
         Order order = new Order();
         System.out.println(order.orderId);
    }
}
class Order {
    int orderId = 3;
    {
         orderId = 4;
    }
}

四、final关键字

①final可以用来修饰的结构:类、方法、变量
②final用来修饰一个类:此类不能被其他类所继承
         比如:String类、System类、StringBuffer类
③final用来修饰方法:表明此方法不可以被重写
         比如:Object类中的getClass();
④final用来修饰变量:此时的“变量”就称为是一个常量
|-final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
|-final修饰局部变量:
尤其是final修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值
⑤static final用来修饰属性:全局常量

例:

package com.atguigu.java3;
public class FinalTest {
    final int width = 10;
    final int WIDTH = 20;
    final int LEFT;
    final int RIGHT;
    // final int DOWN;
    {
         LEFT = 1;
    }
    public FinalTest() {
         RIGHT = 2;
    }
    public FinalTest(int n) {
         RIGHT = n;
    }
//  public void setDown(int down) {
//       this.DOWN = down;
//  }
    public void doWidth() {
         // width = 20;
    }
    public void show() {
         final int NUM = 10;
//  NUM += 20;
    }
    public void show(final int num) {
         System.out.println(num);
    }
    public static void main(String[] args) {
         // Number =20;//编译不通过
         int num = 10;
         num = num + 5;
         FinalTest test = new FinalTest();
//       test.setDown = 3;
         test.show(10);
    }
}
final class FinalA {
}
//class B extends FinalA {
//
//}
//class c extends String{
//  
//}
class AA {
    public final void show() {
    }
}
//class BB extends AA{
//  public void show(){
//       
//  }
//}

五、抽象类与抽象方法

①有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。

在这里插入图片描述|-abstract关键字的使用:

①abstract:抽象的
②可以用来修饰的结构:类、方法
③abstract修饰类:抽象类
|-此类不能实例化
|-抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
|-开发中都会提供抽象类的子类,让子类对象实例化,完成相关操作
④abstract修饰方法:抽象方法
|-抽象方法只有方法的声明,没有方法体
|-包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
|-若子类重写了父类中的所有抽象方法后,此子类方可实例化
|-若子类没有重写父类中的所有抽象方法,则此子类也是一个抽象类,需要使用abstract修饰一下

例:

package com.atguigu.java;
public class AbstractTest {
    public static void main(String[] args) {
         // 一旦person类抽象了,就不可以实例化
//       Person p1 = new Person();
//       p1.eat();
    }
}
abstract class Creature {
    public abstract void breath();
}
abstract class Person extends Creature {
    String name;
    int age;
    public Person() {
    }
    public Person(String name, int age) {
         this.name = name;
         this.age = age;
    }
//  // 不是抽象方法
//  public void eat() {
//
//  }
    // 抽象方法
    public abstract void eat();
    public void walk() {
         System.out.println("人走路!");
    }
}
class Student extends Person {
    public Student(String name, int age) {
         super(name, age);
    }
    public void eat() {
         System.out.println("学生应该多吃有营养的食物!");
    }
    @Override
    public void breath() {
         System.out.println("学生应该呼吸新鲜的没有雾霾的空气!");
    }
}

|-abstract的使用注意:

①abstract不能用来修饰:属性、构造器等结构
②abstract不能用来修饰私有方法、静态方法、final的方法、final的类

|-基本操作

package com.atguigu.exer1;
public abstract class Employee {
    private String name;
    private int id;
    private double salary;
    public Employee() {
         super();
    }
    public Employee(String name, int id, double  salary) {
         super();
         this.name = name;
         this.id = id;
         this.salary = salary;
    }
    public abstract void work();
}
package com.atguigu.exer1;
public class Manager extends Employee {
    private double bonus;// 奖金
    public Manager(double bonus) {
         super();
         this.bonus = bonus;
    }
    public Manager(String name, int id, double  salary, double bonus) {
         super(name, id, salary);
         this.bonus = bonus;
    }
    @Override
    public void work() {
         System.out.println("管理员工,提高公司运行效率!");
    }
}
package com.atguigu.exer1;
public class CommonEmployee extends Employee {
    @Override
    public void work() {
         System.out.println("员工在一线车间生产产品!");
    }
}
package com.atguigu.exer1;
public class EmployeeTest {
    public static void main(String[] args) {
         Employee manager = new Manager("库克",  1001, 5000, 50000);
         manager.work();
         CommonEmployee commonEmployee = new  CommonEmployee();
         commonEmployee.work();
    }
}

|-创建抽象类的匿名子类对象
例:

package com.atguigu.java;
public class PersonTest {
    public static void main(String[] args) {
         method(new Student());// 匿名对象
         Worker worker = new Worker();
         method1(worker);// 非匿名的类非匿名的对象
         method1(new Worker());// 非匿名的类匿名的对象
         // 创建了一匿名子类的对象:p
         Person p = new Person() {
             @Override
             public void breath() {
                 System.out.println("好好呼吸!");
             }
             @Override
             public void eat() {
                 System.out.println("吃东西!");
             }
         };
         method1(p);
         System.out.println("**************");
         // 创建匿名子类的匿名对象
         method1(new Person() {
             @Override
             public void breath() {
                 System.out.println("呼吸新鲜空气!");
             }
             @Override
             public void eat() {
                 System.out.println("吃有营养的食物!");
             }
         });
    }
    public static void method1(Person p) {
         p.eat();
         p.breath();
    }
    public static void method(Student s) {
    }
}
class Worker extends Person {
    @Override
    public void eat() {
         // TODO Auto-generated method stub
    }
    @Override
    public void breath() {
         // TODO Auto-generated method stub
    }
}

|-模版方法的设计模式(TemplateMethod)及应用场景

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
|-当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
|-换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

|-模版方法的设计模式:
例一:

package com.atguigu.java;
public class TemplateTest {
    public static void main(String[] args) {
         Template t = new SubTemplate();
         t.sepndTime();
    }
}
abstract class Template {
    // 计算某段代码执行需要花费的时间
    public void sepndTime() {
         long start = System.currentTimeMillis();
         this.code();// 不确定的部分、易变的部分
         long end = System.currentTimeMillis();
         System.out.println("花费的时间为:" + (end -  start));
    }
    public abstract void code();
}
class SubTemplate extends Template {
    @Override
    public void code() {
         for (int i = 2; i <= 1000; 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);
             }
         }
    }
}

例二:

package com.atguigu.java;
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
    public static void main(String[] args) {
         BankTemplateMethod btm = new DrawMoney();
         btm.process();
         BankTemplateMethod btm2 = new  ManageMoney();
         btm2.process();
    }
}
abstract class BankTemplateMethod {
    // 具体方法
    public void takeNumber() {
         System.out.println("取号排队");
    }
    public abstract void transact(); // 办理具体的业务 //钩子方法
    public void evaluate() {
         System.out.println("反馈评分");
    }
    // 模板方法,把基本操作组合到一起,子类一般不能重写
    public final void process() {
         this.takeNumber();
         this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
         this.evaluate();
    }
}
class DrawMoney extends BankTemplateMethod {
    public void transact() {
         System.out.println("我要取款!!!");
    }
}
class ManageMoney extends BankTemplateMethod {
    public void transact() {
         System.out.println("我要理财!我这里有2000万美元!!");
    }
}

|-抽象类的课后练习
例:

package com.atguigu.exer2;
public abstract class Employee {
    private String name;
    private int number;
    private MyDate birthday;
    public Employee(String name, int number, MyDate  birthday) {
         super();
         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();
    @Override
    public String toString() {
         return "name=" + name + ", number=" +  number + ", birthday=" + birthday.toDateString();
    }
}
package com.atguigu.exer2;
public class MyDate {
    private int year;
    private int month;
    private int day;
    public MyDate(int year, int month, int day) {
         super();
         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 +  "日";
    }
}
package com.atguigu.exer2;
public class SalariedEmployee extends Employee {
    private int monthlySalary;
    public SalariedEmployee(String name, int  number, MyDate birthday) {
         super(name, number, birthday);
         this.monthlySalary = monthlySalary;
    }
    public SalariedEmployee(String name, int  number, MyDate birthday, double monthlySalary) {
         super(name, number, birthday);
    }
    public SalariedEmployee(String name, int  number, MyDate birthday, int monthlySalary) {
         super(name, number, birthday);
         this.monthlySalary = monthlySalary;
    }
    public int getMonthlySalary() {
         return monthlySalary;
    }
    public void setMonthlySalary(int monthlySalary)  {
         this.monthlySalary = monthlySalary;
    }
    @Override
    public double earnings() {
         return monthlySalary;
    }
    public String toString() {
         return "SalariedEmployee [" +  super.toString() + "]";
    }
}
package com.atguigu.exer2;
public class HourlyEmployee extends Employee {
    private int wage;// 每小时的工资
    private int hour;// 月工作的小时数
    public HourlyEmployee(String name, int number,  MyDate birthday) {
         super(name, number, birthday);
    }
    public HourlyEmployee(String name, int number,  MyDate birthday, int wage, int hour) {
         super(name, number, birthday);
         this.wage = wage;
         this.hour = hour;
    }
    public int getWage() {
         return wage;
    }
    public void setWage(int 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() + "]";
    }
}
package com.atguigu.exer2;
import java.util.Calendar;
import java.util.Scanner;
public class PayrollSystem {
    public static void main(String[] args) {
         // 方式一:
//       Scanner scanner = new Scanner(System.in);
//       System.out.println("请输入当月的月份:");
//       int month = scanner.nextInt();
         // 方式二:
         Calendar calendar =  Calendar.getInstance();
         int month =  calendar.get(Calendar.MONTH);// 获取当前的月份
         Employee[] emps = new Employee[2];
         emps[0] = new SalariedEmployee("马森",  1002, new MyDate(1992, 2, 28), 10000);
         emps[1] = new HourlyEmployee("潘雨生",  2001, new MyDate(1991, 12, 9), 60, 240);
         for (int i = 0; i < emps.length; i++) {
             System.out.println(emps[i]);
             double salary = emps[i].earnings();
             System.out.println("月工资为:" +  salary);
             if ((month + 1) ==  emps[i].getBirthday().getMonth()) {
                 System.out.println("生日快乐,奖励一百元!");
             }
         }
    }
}

六、接口(Interface)
|-概述

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

在这里插入图片描述在这里插入图片描述|-接口的使用:

①接口使用Interface来定义
②Java中,接口和类是并列的两个结构
③如何定义接口:定义接口中的成员
|-JDK7及以前:只能定义全局常量和抽象方法
>全局常量:public static final,但是书写时可以省略不写
>抽象方法:public  abastract
|-JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法
④接口中不能定义构造器,意味着接口不可以实例化
⑤开发中,接口通过让类去实现的(implements)方式来使用
|-如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
|-如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
⑥Java可以实现那多个接口---->弥补了Java单继承性的局限性
格式:class AA extends BB implement CC,DD,EE
⑧接口的具体使用体现多态性
⑨接口,实际上可以看作一种规范

例:

package com.atguigu.java1;
public class InterfaceTest {
    public static void main(String[] args) {
         System.out.println(Flyable.MAX_SPEED);
         System.out.println(Flyable.MIN_SPEED);
         // Flyable.MIN_SPEED = 2;
         Plane plane = new Plane();
         plane.fly();
    }
}
interface Flyable {
    // 全局常量
    public static final int MAX_SPEED = 7900;// 第一宇宙速度
    int MIN_SPEED = 1;// 省略了public static final
    // 抽象方法
    public abstract void fly();
    void stop();// 省略了public abstract、
//  public Flyable() {
//        
//   }
}
interface Attackable{
    void attack();
}
class Plane implements Flyable {
    @Override
    public void fly() {
         System.out.println("通过引擎起飞!");
    }
    @Override
    public void stop() {
         System.out.println("驾驶员减速停止!");
    }
}
abstract class kite implements Flyable {
    @Override
    public void fly() {
         // TODO Auto-generated method stub
    }
}
class Bullet extends Object implements  Flyable,Attackable{
    @Override
    public void attack() {
         // TODO Auto-generated method stub
         
    }
    @Override
    public void fly() {
         // TODO Auto-generated method stub
         
    }
    @Override
    public void stop() {
         // TODO Auto-generated method stub
         
    }
    
}
//*******************************************************
interface AA{
    
}
interface BB{
    
}
interface CC extends AA,BB{
    
}

例:
|-接口是一种规范

package com.atguigu.java1;
public class USBTest {
    public static void main(String[] args) {
         Computer com = new Computer();
         Flash flash = new Flash();
         com.transferData(flash);
    }
}
class Computer {
    public void transferData(USB usb) {// USB usb =  new Flash();
         usb.start();
         System.out.println("具体的传输数据的细节!");
         usb.stop();
    }
}
interface USB {
    void start();
    void stop();
}
class Flash implements USB {
    @Override
    public void start() {
         System.out.println("U盘开启工作!");
    }
    @Override
    public void stop() {
         System.out.println("U盘结束工作!");
    }
}
class Printer implements USB {
    @Override
    public void start() {
         System.out.println("打印机开启工作!");
    }
    @Override
    public void stop() {
         System.out.println("打印机结束工作!");
    }
}

|-创建接口匿名实现类的对象

package com.atguigu.java1;
public class USBTest {
    public static void main(String[] args) {
         Computer com = new Computer();
         // 1.创建了接口的非匿名实现类的非匿名对象
         Flash flash = new Flash();
         com.transferData(flash);
         // 2.创建了接口的非匿名实现类的匿名对象
         com.transferData(new Printer());
         // 3.创建了接口的匿名实现类的非匿名对象
         USB phone = new USB() {
             @Override
             public void start() {
                 System.out.println("手机开始工作!");
             }
             @Override
             public void stop() {
                 System.out.println("手机结束工作!");
             }
         };
         com.transferData(phone);
         // 4.创建了接口的匿名实现类的匿名对象
         com.transferData(new USB() {
             @Override
             public void start() {
                 System.out.println("mp3开始工作!");
             }
             @Override
             public void stop() {
                 System.out.println("mp3结束工作!");
             }
         });
    }
}
class Computer {
    public void transferData(USB usb) {// USB usb =  new Flash();
         usb.start();
         System.out.println("具体的传输数据的细节!");
         usb.stop();
    }
}
interface USB {
    void start();
    void stop();
}
class Flash implements USB {
    @Override
    public void start() {
         System.out.println("U盘开启工作!");
    }
    @Override
    public void stop() {
         System.out.println("U盘结束工作!");
    }
}
class Printer implements USB {
    @Override
    public void start() {
         System.out.println("打印机开启工作!");
    }
    @Override
    public void stop() {
         System.out.println("打印机结束工作!");
    }
}

|-接口应用:代理模式
概述:

代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

在这里插入图片描述
应用场景:

|-安全代理:屏蔽对真实角色的直接访问。
|-远程代理:通过代理类处理远程方法调用(RMI)
|-延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

例一:

package com.atguigu.java1;
import java.awt.Checkbox;
public class NetWorkTest {
    public static void main(String[] args) {
         Server server = new Server();
         ProxySrever proxySrever = new  ProxySrever(server);
         proxySrever.browse();
    }
}
interface NetWork {
    public void browse();
}
//被代理类
class Server implements NetWork {
    @Override
    public void browse() {
         System.out.println("真实的服务器访问网络!");
    }
}
//代理类
class ProxySrever implements NetWork {
    private NetWork work;
    public ProxySrever(NetWork work) {
         this.work = work;
    }
    public void Check() {
         System.out.println("联网之前的检查工作!");
    }
    @Override
    public void browse() {
         Check();
         work.browse();
    }
}

例二:

package com.atguigu.java1;
public class StaticProxyTest {
    public static void main(String[] args) {
         Star s = new Proxy(new RealStar());
         s.confer();
         s.signContract();
         s.bookTicket();
         s.sing();
         s.collectMoney();
    }
}
interface Star {
    void confer();// 面谈
    void signContract();// 签合同
    void bookTicket();// 订票
    void sing();// 唱歌
    void collectMoney();// 收钱
}
class RealStar implements Star {
    public void confer() {
    }
    public void signContract() {
    }
    public void bookTicket() {
    }
    public void sing() {
         System.out.println("明星:歌唱~~~");
    }
    public void collectMoney() {
    }
}
class Proxy implements Star {
    private Star real;
    public Proxy(Star real) {
         this.real = real;
    }
    public void confer() {
         System.out.println("经纪人面谈");
    }
    public void signContract() {
         System.out.println("经纪人签合同");
    }
    public void bookTicket() {
         System.out.println("经纪人订票");
    }
    public void sing() {
         real.sing();
    }
    public void collectMoney() {
         System.out.println("经纪人收钱");
    }
}

|-接口和抽象类之间的对比:
在这里插入图片描述|-练习
例:

package com.atguigu.exer3;
public interface CompareObject {
    //若返回值是 0 代表相等 若为正数 代表当前对象大;负数代表当前对象小
    public int compareTo (Object o);
}
package com.atguigu.exer3;
public class Circle {
    private Double radius;
    public Double getRadius() {
         return radius;
    }
    public void setRadius(Double radius) {
         this.radius = radius;
    }
    public Circle() {
         super();
    }
    public Circle(Double radius) {
         super();
         this.radius = radius;
    }
}
package com.atguigu.exer3;
import javax.management.RuntimeErrorException;
public class ComparableCircle extends Circle  implements CompareObject {
    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());
             // 方式一:
//           if (this.getRadius() > c.getRadius())  {
//               return 1;
//           } else if (this.getRadius() <  c.getRadius()) {
//               return -1;
//           } else {
//               return 0;
//           }
             // 方式二:
             // 当属性radius声明为Double类型时,可以调用包装类的方法
             return  this.getRadius().compareTo(c.getRadius());
         } else {
             return 0;
             // throw new RuntimeException("传入的数据类型不匹配!");
         }
    }
}
package com.atguigu.exer3;
public class ComparableCircleTest {
    public static void main(String[] args) {
         ComparableCircle c1 = new  ComparableCircle(3.4);
         ComparableCircle c2 = new  ComparableCircle(3.6);
         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一样大");
         }
         
         int compareValue1 = c1.compareTo(new  String("AA"));
         System.out.println(compareValue1);
    }
}

|-Java8中接口的新特性

①接口中定义的静态方法,只能通过接口来调用
②通过实现类的对象,可以调用接口中的默认方法,如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写的方法
③如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法(类优先原则)
④如果实现类实现了多个接口,而这多个接口定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错--->接口冲突,这就需要我们必须在实现类中重写此方法

例:

package com.atguigu.java8;
public interface CompareA {
    //静态方法
    public static void method1() {
         System.out.println("CompareA:北京");
    }
    //默认方法
    public default void method2() {
         System.out.println("CompareA:上海");
    }
    
    default void method3() {
         System.out.println("CompareA:上海");
    }
}
package com.atguigu.java8;
public interface CompareB {
    default void method3() {
         System.out.println("CompareB:上海");
    }
}
package com.atguigu.java8;
public class SuperClass {
public void method3() {
    System.out.println("SuperClass:北京");
}
}
package com.atguigu.java8;
public class SubClassTest {
    public static void main(String[] args) {
         SubClass s = new SubClass();
         // s.method1();
         // 接口中定义的静态方法,只能通过接口来调用
         CompareA.method1();
         // 通过实现类的对象,可以调用接口中的默认方法,
         // 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写的方法
         s.method2();
         // 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
         // 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法(类优先原则)
         s.method3();
    }
}
class SubClass extends SuperClass implements  CompareA, CompareB {
    public void method2() {
         System.out.println("SubClass:上海");
    }
    public void method3() {
         System.out.println("SubClass:深圳");
    }
    // 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
    public void myMethod() {
         method3();// 调用自己定义的重写的方法
         super.method3();// 调用的是父类中声明的
         // 调用接口中的默认方法
         CompareA.super.method3();
         CompareA.super.method3();
    }
}

|-Java8接口新特性的应用

package com.atguigu.java8;
interface Filial {// 孝顺的
    default void help() {
         System.out.println("老妈,我来救你了");
    }
}
interface Spoony {// 痴情的
    default void help() {
         System.out.println("媳妇,别怕,我来了");
    }
}
class Father {
    public void help() {
         System.out.println("儿子,救我媳妇!");
    }
}
class Man implements Filial, Spoony {
    @Override
    public void help() {
         System.out.println("我该救谁呢?");
         Filial.super.help();
         Spoony.super.help();
    }
}

七、内部类
说明:

①Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
②内部类的分类:成员内部类(静态、非静态)VS局部内部类(方法内、代码块内、构造器内)
③成员内部类:
|-一方面,作为外部类的成员:
>调用外部类的结构
>可以被static修饰
>可以被四种不同的权限修饰
|-另一方面分,作为一个类出现:
>类内可以定义属性、方法、构造器等
>可以被final修饰,表示此类不能被继承,反之,不使用final就可以被继承
>可以被abstract修饰

例:

package com.atguigu.java2;
public class InnerClassTest {
}
class Person {
    // 静态成员内部类
    static class Dog {
    }
    // 非静态内部类
    class Bird {
    }
    public void method() {
         // 局部内部类
         class AA {
         }
    }
    {
         // 局部内部类
         class BB {
         }
    }
    // 局部内部类
    public Person() {
         class CC {
         }
    }
}

例:

package com.atguigu.java2;
public class InnerClassTest {
}
class Person {
    String name;
    int age;
    public void eat() {
         System.out.println("人吃饭!");
    }
    // 静态成员内部类
    abstract static class Dog {
         String name;
         int age;
         public void show() {
             System.out.println("卡拉是条狗!");
         }
    }
    // 非静态成员内部类
    class Bird {
         String nane;
         public Bird() {
         }
         public void sing() {
             System.out.println("我是一只小小鸟!");
             Person.this.eat();// 调用外部类的非静态属性
         }
    }
    public void method() {
         // 局部内部类
         class AA {
         }
    }
    {
         // 局部内部类
         class BB {
         }
    }
    // 局部内部类
    public Person() {
         class CC {
         }
    }
}

|-实例化成员内部类
例:

package com.atguigu.java2;
import com.atguigu.java2.Person.Dog;
public class InnerClassTest {
    public static void main(String[] args) {
         // 创建静态成员内部类
         Person.Dog dog = new Person.Dog();
         dog.show();
         // 创建非静态成员内部类
         Person p = new Person();
         Person.Bird bird = p.new Bird();
         bird.sing();
    }
}
class Person {
    String name;
    int age;
    public void eat() {
         System.out.println("人吃饭!");
    }
    // 静态成员内部类
    static class Dog {
         String name;
         int age;
         public void show() {
             System.out.println("卡拉是条狗!");
         }
    }
    // 非静态成员内部类
    class Bird {
         String nane;
         public Bird() {
         }
         public void sing() {
             System.out.println("我是一只小小鸟!");
             Person.this.eat();// 调用外部类的非静态属性
         }
    }
    public void method() {
         // 局部内部类
         class AA {
         }
    }
    {
         // 局部内部类
         class BB {
         }
    }
    // 局部内部类
    public Person() {
         class CC {
         }
    }
}

|-成员内部类中调用外部类的结构

package com.atguigu.java2;
import com.atguigu.java2.Person.Dog;
public class InnerClassTest {
    public static void main(String[] args) {
         // 创建静态成员内部类
         Person.Dog dog = new Person.Dog();
         dog.show();
         // 创建非静态成员内部类
         Person p = new Person();
         Person.Bird bird = p.new Bird();
         bird.sing();
         System.out.println();
         bird.display("黄鹂");
    }
}
class Person {
    String name = "小明";
    int age;
    public void eat() {
         System.out.println("人吃饭!");
    }
    // 静态成员内部类
    static class Dog {
         String name;
         int age;
         public void show() {
             System.out.println("卡拉是条狗!");
         }
    }
    // 非静态成员内部类
    class Bird {
         String name = "杜鹃";
         public Bird() {
         }
         public void sing() {
             System.out.println("我是一只小小鸟!");
             Person.this.eat();// 调用外部类的非静态属性
         }
          //成员内部类中调用外部类的结构
         public void display(String name) {
             System.out.println(name);// 方法的形参
             System.out.println(this.name);// 内部类的属性
             System.out.println(Person.this.name);// 外部类的属性
         }
    }
    public void method() {
         // 局部内部类
         class AA {
         }
    }
    {
         // 局部内部类
         class BB {
         }
    }
    // 局部内部类
    public Person() {
         class CC {
         }
    }
}

|-局部内部类的使用

package com.atguigu.java2;
public class InnerClassTest1 {
    // 返回一个实现了Comparable接口类的对象
    public Comparable geComparable() {
         // 创建一个实现了Comparable接口的类:局部内部类
         // 方式一:
//  class MyComparable implements Comparable{
//
//       @Override
//       public int compareTo(Object o) {
//           // TODO Auto-generated method stub
//           return 0;
//       }
//       
//  }
//  return new MyComparable();
         // 方式二:
         return new Comparable() {
             @Override
             public int compareTo(Object o) {
                 // TODO Auto-generated method stub
                 return 0;
             }
         };
    }
}

|-局部内部类使用的注意点:

①在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量,要求此局部变量声明为final

例:

package com.atguigu.java;
public class InnerClassTest {
    public void method() {
         // 局部变量
         final int num = 10;
         class AA {
             public void show() {
                 System.out.println(num);
             }
         }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值