Java学习笔记上篇(atguigu)

 

目录

 

1 Java语言概述

2 基本语法

2.1 关键字 & 标识符 

 2.2变量

 2.3运算符

2.4 流程控制 

2.4.1 分支结构

2.4.2 循环结构

 2.4.3 break &continue

 2.5 数组

2.5.1 二维数组 

 2.5.2 数组常见异常

 2.5.3 数组的常用的算法问题

3 面向对象(一)

3.1 类及对象

3.2 方法的重载

 3.4 java的值传递机制

3.5 面向对象的特征一:封装性

 3.6 构造器

3.7 this关键字

 3.8 package & import

4 面向对象(二)

4.1 面向对象的特征二:继承性

 4.1.1 方法的重写

 4.2 关键字super

4.3 子类对象实例化的全过程

4.4 面向对象特征三:多态性

 4.5 Object类

 4.6 包装类

 5 面向对象(三)

5.1 static关键字

 5.1.1 单例模式

 5.2 main()方法

 5.3 代码块

 5.4 final关键字

5.5 抽象:abstract

 5.5.1 模板方法的设计模式

 5.6 接口interface

 5.6.1 工厂方法的设计模式

 5.6.2 代理模式

 5.7 内部类

 总结


1 Java语言概述

1.软件 : 系统软件  vs  应用软件

2.人与计算机做交互:使用计算机语言。
        图形化界面  vs  命令行方式  dir  md:创建文件夹 rd:删除目录  cd  cd..  cd/:回退到根目录  del:删除文件  exit:退出dos命令行
        
3.语言的分类:第一代:机器语言  第二代:汇编语言  第三代:高级语言(面向过程  --- 面向对象)

4.java语言的特性:①面向对象性 ②健壮性 ③跨平台性(write once ,run anywhere)---JVM(java虚拟机)

5.安装JDK及配置path环境变量
   1)傻瓜式安装JDK。 
   2)path:window操作系统在执行命令时所要搜寻的路径。
      我们需要将jdk中bin目录所在的路径:D:\Java\jdk1.7.0_07\bin 保存在path环境变量下。
   3)测试:在命令行窗口,任意的文件目录下,执行javac.exe 或者java.exe都可以调用成功。


>河床好比操作底层,jdk好比是河水,java应用程序好比是船。


6.如何编写并运行第一个java程序
  【过程】编写----编译----运行
1)编写:每一个java文件都是.java结尾的,称为源文件【HelloWorld.java】。java程序就存在于源文件中
 

 public class HelloWorld{
    //程序的主方法,是程序的入口
      public static void main(String args[]){
        //要执行的代码
           System.out.println("HelloWorld");
          }
    }

 

注意点:
Java源文件以“java”为扩展名。源文件的基本组成部分是类(class),如本类中的HelloWorld类。
一个源文件中最多只能有一个public类。其它类的个数不限,如果源文件包含一个public类,则文件名必须按该类名命名。
Java应用程序的执行入口是main()方法。它有固定的书写格式:public static void main(String[] args)  {...}
Java语言严格区分大小写。
Java方法由一条条语句构成,每个语句以“;”结束。
大括号都是成对出现的,缺一不可。

2)编译: 在源文件所在的目录下,执行javac.exe 源文件名.java;生成诸多个.class结尾的字节码文件
3)运行:生成的字节码文件通过java.exe解释执行

javac hello.java
java hello

7.会调试程序中出现的问题

8.注释:  ①单行注释 //   ②多行注释  /*    */   (多行注释不能够嵌套)  
              ③文档注释 /**    */        javadoc  -d 文件目录名 -author -version 源文件名.java;

9.JDK提供的关于旗下所有的包、类的文档:API

2 基本语法

2.1 关键字 & 标识符 

关键字:被Java语言赋予了特殊含义,用做专门用途的字符串(单词)

保留字:

标识符:凡是自己可以起名字的地方都叫标识符


命名的规则:(一定要遵守,不遵守就会报编译的错误)
由26个英文字母大小写,0-9 ,_或 $ 组成  
数字不可以开头。
不可以使用关键字和保留字,但能包含关键字和保留字。
Java中严格区分大小写,长度无限制。
标识符不能包含空格。

Java中的名称命名规范:(不遵守,也不会出现编译的错误)
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ

 

 2.2变量

1.java中变量按照数据类型来分类:基本数据类型 vs  引用数据类型(数组 类 接口)
  >基本数据类型:
     整型:byte(8 bit)  short   int(默认类型)   long(+L)
     浮点型:float(+F) double (默认类型)
     字符型:char(‘ ’)
     布尔类型: boolean(只能取值为true 或false,不能取null)

    补充:按照在类中存在的位置的不同:成员变量 vs 局部变量

2.进制(了解)
    十进制  二进制  八进制 十六进制

   二进制:计算机底层都是用二进制来存储、运算。
    >二进制 与十进制之间的转换。
    >二进制在底层存储:正数、负数都是以补码的形式存储的。(原码、反码、补码)
    >四种进制间的转换

3.变量的运算:
   ①自动类型转换:容量小的数据类型自动转换为容量大的数据类型。
    short s = 12;
     int i = s + 2;
    注意:byte  short char之间做运算,结果为int型!
   ②强制类型转换:是①的逆过程。使用“()”实现强转。

long l1 = 1234L;
int m1 = (int)l1;

 2.3运算符

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
算术运算符:
+  -  + - * / % ++ -- +
注意:1) /:   int i = 12;  i = i / 5;
      2) %:最后的符号只跟被模数相同
      3)前++:先+1,后运算     后++:先运算,后+1
      4)+:String字符串与其他数据类型只能做连接运算,且结果为String类型。sysout('*' + '\t' + '*'); vs  sysout("*" + '\t' + '*');
                 
赋值运算符:
=    +=   -=  *=    /=   %=
int i= 12;
i  = i * 5;
i *= 5;//与上一行代码同样的意思
【特别地】
short s = 10;
s = s + 5;//报编译的异常
s = (short)(s + 5);
s += 5;//s = s + 5,但是结果不会改变s的数据类型。


比较运算符(关系运算符)
==  >   <  >=   <=    instanceof  

【注意】区分==  与 =  区别。
进行比较运算操作以后,返回一个boolean类型的值
4>=3  表达的是4 > 3或者 4 = 3.结果是true。

if(i > 1 && i < 10){  }   
不能写为:if(1 < i < 10){}

逻辑运算符(运算符的两端是boolean值)
&   &&短路与  |  ||  ^异或 !
【注意】区分 &  与 && 的区别,以及 |  与 || 的区别
  我们使用的时候,选择&& , ||

位运算符(两端是数值类型的数据)
<<   >>    >>>  &  |   ^  ~(取反+1)
【例子】1.如何交换m = 12和n = 5的值
             2.将60转换为十六进制输出。

三元运算符
(条件表达式)? 表达式1 : 表达式2;
1)既然是运算符,一定会返回一个结果,并且结果的数据类型与表达式1,2的类型一致
2)表达式1与表达式2 的数据类型一致。
3)使用三元运算符的,一定可以转换为if-else。反之不一定成立。
例子:获取两个数的较大值;获取三个数的最大值。
 

2.4 流程控制 

1.顺序结构
  >程序从上往下的顺序执行

2.分支结构:
    if-else   
    switch-case

3.循环结构:
  while(){}
  do{}while()
  for(;;){}

2.4.1 分支结构

1.条件判断:
①if(条件表达式){   }

②if(条件表达式){
    //执行的语句1;
  }else{
    //执行的语句2;
  }

③if(条件表达式1){
    //执行的语句1;
  }else if(条件表达式2){
    //执行的语句2;
  }else if( 条件表达式3){
    //执行的语句3;
  }...
  }else{
    //执行的语句;
  }

【注意】
  1.一旦满足某个条件表达式,则进入其执行语句块执行,执行完毕,跳出当前的条件判断结构,不会执行其以下的条件结构语句。
  2.如果诸多个条件表达式之间为“互斥”关系,多个结构可以上下调换顺序
    如果诸多个条件表达式之间为“包含”关系,要求条件表达式范围小的写在范围大的上面。

2.选择结构
switch(变量){
    case 值1:
    
    //break;
    case 值2:
    
    //break;
    ...
    default:
    
    break;
}

【注意】
1.变量可以为如下的数据类型:byte  short int char 枚举 String 
2.case后只能填写变量的值,不能写范围。
3.default是可以省略的。并且其位置也是灵活的,但是通常将其放在case语句之后。
4.一旦满足某个case语句,则进入执行其操作。直至遇到break或者程序终止。
5.若要判断的语句的条件满足switch变量的数据类型,且值不多的情况下,建议选择switch-case .
  除此之外,选择if-else.

2.4.2 循环结构

1.格式:
①初始化条件
②循环条件
③迭代部分
④循环体


for(①;②;③){
    ④
}


while(②){
    ④
    ③
}


do{
    ④
    ③
}while(②);

注:1.不同的循环结构之间可以相互转换
      2.while和do-while的区别:do-while程序至少会执行一次

2.嵌套循环:循环结构还可以声明循环。让内层循环结构整体充当外层循环的循环体。
若外层循环执行m次,内层循环执行n次,整个程序执行m*n次。

【题目】
******
******
******
for(int i = 0;i < 3;i++){
    for(int j = 0;j < 6;j++){
        System.out.print("*");
    }
    System.out.println();
}
说明:外层循环控制行数,内层循环控制列数
【例题】1.九九乘法表  2.输出100内的质数。(两种)


3.无限循环结构
for(;;){
   ...
   if(  ){
    break;
   }
   ...
}
或者
while(true){
   ...
   if(  ){
    break;
   }
   ...
}
往往我们在无限循环结构内部提供循环的终止条件,使用break关键字。否则,此循环将无限制的执行下去,形成死循环!
死循环是我们要避免的。

 2.4.3 break &continue

break:
>使用switch-case结构或者循环结构中
>在循环结构中,一旦执行到break,就跳出当前循环。

continue:使用在循环结构中
>一旦执行到continue,就跳出当次循环。


for(int i = 1;i <= 10;i++){
    if(i % 4 == 0){
        //break; //123
        continue;  //123567910
    }

    System.out.print(i);
}


在嵌套循环中,使用带标签的break和continue。
label:for(int i = 1;i < 5;i++){
    for(int j = 1;j <= 10;j++){
        if(j % 4 == 0){
            //break;
            //continue;
            //break label;
            continue label;
        }
        System.out.print(j);
    }
    System.out.println();
}

 2.5 数组

数组:相同数据类型的数据的组合。

如: int score1 = 72;
       int score2 = 90;
       int score3 = 59;

使用数组:
1.数组的初始化
     int[] scores1 = new int[]{72,90,59};//静态初始化:在声明并初始化数组与给数组相应的元素赋值操作同时进行。
     int scores2[] = new int[3];//动态初始化:在声明并初始化数组与给数组相应的元素赋值操作分开进行。
     scores2[0] = 72;
     scores2[1] = 90;
     scores2[2] = 59;
    //声明数组的错误写法:
     1)String[] names = new String[5]{"AA","BB","CC"};
     2)int i[10];
     3)int i = new int[];
注:不管是动态还是静态初始化数组,一定在创建的时候,就指明了数组的长度!
2.如何引用数组元素:通过数组的下角标的方式。下角标从0开始,到n-1结束。其中n为数组的长度。
3.数组的长度:通过数组的属性length来调用。
System.out.println(scores2.length);//3
4.如何遍历数组
for(int i = 0;i < scores1.length;i++){
    System.out.println(scores1[i]);
}
5.关于数组元素的默认初始化值
1)byte short int long 而言:0
2)float double 而言:0.0
3)char而言:空格
4)boolean而言:false
5)引用类型变量而言:null

6.数组的内存结构


对于数组来讲:

2.5.1 二维数组 

1.声明并初始化
//一维:
int[] i = new int[12];
i[0] = 12;

int[] j = new int[]{12,3};

//二维:
1)String[][] str = new String[4][3]; //4行3列

2)String[][] str1 = new String[4][];
str1[0] = new String[3];
...
str1[3] = new String[5];
3)
int[][] arr = new int[][]{{1,2,3},{4,5},{6}};

2.如何引用二维数组的元素:arr[1][0] = 12;

3.二维数组的长度:arr.length;//3
arr[1].length;//2

4.遍历二维数组

for(int i = 0;i < arr.length;i++){
    for(int j = 0;j < arr[i].length;j++){
        System.out.print(arr[i][j] + "\t");    
    }
    System.out.println();
 }
5.二维数组的结构:

 2.5.2 数组常见异常

//1.数组下标越界的异常:java.lang.ArrayIndexOutOfBoundsException
        int[] i = new int[10];
//        i[0] = 90;
//        i[10] = 99;
        
//        for(int m = 0;m <= i.length;m++){
//            System.out.println(i[m]);
//        }
        //2.空指针的异常:NullPointerException
        //第一种:
//        boolean[] b = new boolean[3];
//        b = null;
//        System.out.println(b[0]);
        
        //第二种:
//        String[] str = new String[4];
//        //str[3] = new String("AA");//str[3] = "AA";
//        System.out.println(str[3].toString());
        
        //第三种:
        int[][] j = new int[3][];
        j[2][0] = 12;

 2.5.3 数组的常用的算法问题

1.求数组元素的最大值、最小值、和、平均数

2.数组的复制和反转

情况1:

情况2:(如何实现复制)

数组的反转:
// 数组元素的反转
// for(int i = 0;i < arr.length/2;i++){
// int temp = arr[i];
// arr[i] = arr[arr.length-1 - i];
// arr[arr.length - 1 - i] = temp;
// }
for (int x = 0, y = arr.length - 1; x < y; x++, y--) {
    int temp = arr[x];
    arr[x] = arr[y];
    arr[y] = temp;
}

拓展:String str = "abcdefg";

数组的排序:
插入排序
直接插入排序、折半插入排序、Shell排序

交换排序
冒泡排序、快速排序(或分区交换排序)

选择排序
简单选择排序、堆排序

归并排序

基数排序


// 使用冒泡排序使数组元素从小到大排列
//        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;
//                }
//            }
//        }
//        //使用直接选择排序使数组元素从小到大排列
//        for(int i = 0; i < arr.length - 1; i++){
//            int t = i;//默认i处是最小的
//            for(int j = i;j < arr.length;j++){
//                //一旦在i后发现存在比其小的元素,就记录那个元素的下角标
//                if(arr[t] > arr[j]){
//                    t = j;
//                }
//            }
//            if(t != i){
//                int temp = arr[t];
//                arr[t] = arr[i];
//                arr[i] = temp;
//            }
//        }

还可以调用:Arrays工具类:Arrays.sort(arr);

3 面向对象(一)

3.1 类及对象

1.关于于类的设计

2.类的组成成分:
   1) 属性(成员变量,Field)
   2)方法(成员方法,函数,Method)

2.1属性
     *   成员变量 vs 局部变量
 *   相同点:1.遵循变量声明的格式: 数据类型 变量名 = 初始化值
 *          2.都有作用域
 *   不同点:1.声明的位置的不同 :成员变量:声明在类里,方法外
 *                           局部变量:声明在方法内,方法的形参部分,代码块内
 *         2.成员变量的修饰符有四个:public private protected 缺省
 *           局部变量没有修饰符,与所在的方法修饰符相同。
 *         3.初始化值:一定会有初始化值。
 *             成员变量:如果在声明的时候,不显式的赋值,那么不同数据类型会有不同的默认初始化值。
 *            byte short int long ==>0
 *            float double ==>0.0
 *            char ==>空格
 *            boolean ==>false 
 *            引用类型变量==>null
 *          局部变量:一定要显式的赋值。(局部变量没有默认初始化值)
 *         4.二者在内存中存放的位置不同:成员变量存在于堆空间中;局部变量:栈空间中
 *         
 * 总结:关于变量的分类:1)按照数据类型的不同:基本数据类型(8种)  & 引用数据类型
 *                    2)按照声明的位置的不同:成员变量 & 局部变量

2.2 方法:提供某种功能的实现
 *    1)实例:public void eat(){//方法体}
 *           public String getName(){}
 *           public void setName(String n){}
 *      格式:权限修饰符 返回值类型(void:无返回值/具体的返回值) 方法名(形参){}
 *      
 *    2)关于返回值类型:void:表明此方法不需要返回值
 *                   有返回值的方法:在方法的最后一定有return + 返回值类型对应的变量
 *       记忆:void 与return不可以同时出现一个方法内。像一对“冤家”。
 * 
 *       3)方法内可以调用本类的其他方法或属性,但是不能在方法内再定义方法!


3.面向对象编程的思想的落地法则一
1)设计并创建类及类的成分
2)实例化类的对象
3)通过“对象.属性”或"对象.方法"的形式完成某项功能

4.类的初始化的内存解析
4.1  内存划分的结构:
      栈(stack):局部变量 、对象的引用名、数组的引用名
      堆(heap):new 出来的“东西”(如:对象的实体,数组的实体),含成员变量
      方法区:含字符串常量
      静态域:声明为static的变量

4.2 理解的基础上,学会基本的创建的类的对象在内存中的运行。

3.2 方法的重载

 * 方法的重载(overload)
 * 要求:1.同一个类中 2.方法名必须相同 3.方法的参数列表不同(①参数的个数不同②参数类型不同)
 * 补充:方法的重载与方法的返回值类型没有关系!

//如下的四个方法构成重载
//定义两个int型变量的和
public int getSum(int i,int j){
    return i + j;
}
//定义三个int型变量的和
public int getSum(int i,int j,int k){
    return i + j + k;
}
//定义两个double型数据的和
public double getSum(double d1,double d2){
    return d1 + d2;
}

//定义三个double型数组的和
public void getSum(double d1,double d2,double d3){
    System.out.println(d1 + d2 + d3);
}
//不能与如上的几个方法构成重载
//    public int getSum1(int i,int j,int k){
//        return i + j + k;
//    }
//    public void getSum(int i,int j,int k){
//        System.out.println(i + j + k);
//    }


//以下的两个方法构成重载。
public void method1(int i,String str){
    
}
public void method1(String str1,int j){
    
}

 3.3 可变个数的形参的方法

 * 可变个数的形参的方法:
 * 1.格式:对于方法的形参: 数据类型 ... 形参名
 * 2.可变个数的形参的方法与同名的方法之间构成重载
 * 3.可变个数的形参在调用时,个数从0开始,到无穷多个都可以。
 * 4.使用可变多个形参的方法与方法的形参使用数组是一致的。
 * 5.若方法中存在可变个数的形参,那么一定要声明在方法形参的最后。
 * 6.在一个方法中,最多声明一个可变个数的形参。


    //如下四个方法构成重载
    //在类中一旦定义了重载的可变个数的形参的方法以后,如下的两个方法可以省略
//    public void sayHello(){
//        System.out.println("hello world!");
//    }
//    public void sayHello(String str1){
//        System.out.println("hello " + str1);
//    }
    //可变个数的形参的方法
    public void sayHello(String ... args){
        for(int i = 0;i < args.length;i++){
            System.out.println(args[i] + "$");
        }
        //System.out.println("=====");
    }
    
    public void sayHello(int i,String ... args){
    //public void sayHello(String ... args,int i){
        System.out.println(i);
        
        for(int j = 0;j < args.length;j++){
            System.out.println(args[j] + "$");
        }
    }
    
    public void sayHello1(String[] args){
        for(int i = 0;i < args.length;i++){
            System.out.println(args[i]);
        }
    }

 3.4 java的值传递机制

重点、难点


* 方法的参数传递(重点、难点)
 * 1.形参:方法声明时,方法小括号内的参数
 *   实参:调用方法时,实际传入的参数的值
 *   
 * 2.规则:java中的参数传递机制:值传递机制
 *   1)形参是基本数据类型的:将实参的值传递给形参的基本数据类型的变量
 *   2)形参是引用数据类型的:将实参的引用类型变量的值(对应的堆空间的对象实体的首地址值)传递给形参的引用类型变量。
 
【典型例题1】

public static void main(String[] args) {
    TestArgsTransfer tt = new TestArgsTransfer();
    
    int i = 10;
    int j = 5;
    System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
    
//        //交换变量i与j的值
//        int temp = i;
//        i = j;
//        j = temp;

    tt.swap(i, j);//将i的值传递给m,j的值传递给n
    
    
    System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
    
}
//定义一个方法,交换两个变量的值
public void swap(int m,int n){
    int temp = m;
    m = n;
    n = temp;
    System.out.println("m:" + m + " n:" + n);

}

使用swap方法以后:

【典型例题2】
public class TestArgsTransfer1 {
    public static void main(String[] args) {
        TestArgsTransfer1 tt = new TestArgsTransfer1();
        DataSwap ds = new DataSwap();
        
        System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
        
        tt.swap(ds);
        System.out.println(ds);
        
        System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
        
    }
    //交换元素的值
    public void swap(DataSwap d){
        int temp = d.i;
        d.i = d.j;
        d.j = temp;
        System.out.println(d);//打印引用变量d的值
    }
}

class DataSwap{
    int i = 10;
    int j = 5;
}

3.5 面向对象的特征一:封装性

 * 一、面向对象的特征一:封装与隐藏
 * 问题:当创建了类的对象以后,如果直接通过"对象.属性"的方式对相应的对象属性赋值的话,可能会出现不满足实际
 * 情况的意外,我们考虑不让对象来直接作用属性,而是通过"对象.方法"的形式,来控制对象对属性的访问。实际
 * 情况中,对属性的要求就可以通过方法来体现。
 * 
二、面向对象思想的落地法则二
(封装性的思想)①将类的属性私有化,②提供公共的方法(setter & getter)来实现调用。

三、四种权限修饰符
1.权限从大到小为:public   protected   缺省   private  
2.四种权限都可以用来修饰属性、方法、构造器
3.修饰类的话:public 缺省

 3.6 构造器

 * 一、类的第三个成员:构造器(constructor 构造方法)   construction  CCB  ICBC   oop
 * constructor:建造者 
 * 构造器的作用:①创建对象 ②给创建的对象的属性赋值
 * 
 * 1.设计类时,若不显式声明类的构造器的话,程序会默认提供一个空参的构造器.
 * 2.一旦显式的定义类的构造器,那么默认的构造器就不再提供。
 * 3.如何声明类的构造器。格式:权限修饰符  类名(形参){ }
 * 4.类的多个构造器之间构成重载


 * 二、类对象的属性赋值的先后顺序:①属性的默认初始化 属性的显式初始化通过构造器给属性初始化
 *                           ④通过"对象.方法"的方式给属性赋值

3.7 this关键字

this:
1.使用在类中,可以用来修饰属性、方法、构造器
2.表示当前对象或者是当前正在创建的对象
3.当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
4.在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
5.在构造器中使用“this(形参列表)”显式的调用本类中重载的其它的构造器
   >5.1 要求“this(形参列表)”要声明在构造器的首行!
   >5.2 类中若存在n个构造器,那么最多有n-1构造器中使用了this。

public class TestPerson {
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println(p1.getName() + ":" + p1.getAge());
        
        Person p2 = new Person("BB",23);
        int temp = p2.compare(p1);
        System.out.println(temp);
    }
}
class Person{
    
    private String name;
    private int age;
    
    public Person(){
        this.name = "AA";
        this.age = 1;
    }
    
    public Person(String name){
        this();
        this.name = name;
    }
    public Person(String name,int age){
        this(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 eat(){
        System.out.println("eating");
    }
    public void sleep(){
        System.out.println("sleeping");
        this.eat();
    }
    //比较当前对象与形参的对象的age谁大。
    public int compare(Person p){
        if(this.age > p.age)
            return 1;
        else if(this.age < p.age)
            return -1;
        else
            return 0;
    }
    
}

 3.8 package & import

/*
 * package:声明源文件所在的包,写在程序的第一行。
 *   每“.”一次,表示一层文件目录。
 *   包名都要小写。
 *   
 * import:
 * 1)显式导入指定包下的类或接口
 * 2)写在包的声明和源文件之间
 * 3)如果需要引入多个类或接口,那么就并列写出
 * 4)如果导入的类是java.lang包下的,如:System String Math等,就不需要显式的声明。
 * 5)理解.*的概念。比如java.util.*;
 * 6)如何处理同名类的导入。如:在util包和sql包下同时存在Date类。
 * 7)import static 表示导入指定类的static的属性或方法
 * 8)导入java.lang.*只能导入lang包下的所有类或接口,不能导入lang的子包下的类或接口
 */
//import java.util.Scanner;
//import java.util.Date;
//import java.util.List;
//import java.util.ArrayList;
import java.lang.reflect.Field;
import java.util.*;
import static java.lang.System.*;
public class TestPackageImport {
    public static void main(String[] args) {
        out.println("helloworld");
        Scanner s = new Scanner(System.in);
        s.next();
        
        Date d = new Date();
        List list = new ArrayList();
        
        java.sql.Date d1 = new java.sql.Date(522535114234L);
        
        Field f = null;
    }
}

4 面向对象(二)

4.1 面向对象的特征二:继承性

1.为什么要有继承性:


2.通过"class A extends B"类实现类的继承。
 *   子类:A  父类(或基类 SuperClass):B
 *   
 * 3.子类继承父类以后,父类中声明的属性、方法,子类就可以获取到。
 *    明确:当父类中有私有的属性或方法时,子类同样可以获取得到,只是由于封装性的设计,使得子类不可以直接
 *        调用罢了

 *   子类除了通过继承,获取父类的结构之外,还可以定义自己的特有的成分。
 *   
 *   extends:子类是对父类功能的“扩展”,明确子类不是父类的子集。
 *   
 * 4.java中的继承性只支持单继承:一个类只能继承一个父类。反之,一个父类可以有多个子类。
 * 5.子父类是相对的概念。

 4.1.1 方法的重写

1.方法的重写(override orverwrite)   vs   重载(overload)

【面试题】方法的重载与重写的区别?
重载:“两同一不同”:同一个类,同一个方法名,不同的参数列表。 注:方法的重载与方法的返回值无关!
         >构造器是可以重载的
重写:(前提:在继承的基础之上,子类在获取了父类的结构以后,可以对父类中同名的方法进行“重构”)
        方法的返回值,方法名,形参列表形同;权限修饰符不小于父类的同名方法;子类方法的异常类型不大于父类的;
        两个方法要同为static或同为非static。


public void method1(String[] str,int age) throws Exception{

}

class Cirlce{
   //求圆的面积
    public double findArea(){   


    }

}

class Cylinder extends Circle{
      //求圆柱的表面积
      public double findArea(){
      }
}

 4.2 关键字super

1.super,相较于关键字this,可以修饰属性、方法、构造器

2.super修饰属性、方法:在子类的方法、构造器中,通过super.属性或者super.方法的形式,显式的调用父类的指定
   属性或方法。尤其是,当子类与父类有同名的属性、或方法时,调用父类中的结构的话,一定要用“super.”

3.通过“super(形参列表)”,显式的在子类的构造器中,调用父类指定的构造器!
>任何一个类(除Object类)的构造器的首行,要么显式的调用本类中重载的其它的构造器“this(形参列表)”或显式的调用父类中
指定的构造器“super(形参列表)”,要么默认的调用父类空参的构造器"super()"

>建议在设计类时,提供一个空参的构造器!

4.3 子类对象实例化的全过程

//子类对象实例化的全过程
public class TestDog {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.setAge(10);
        d.setName("小明");
        d.setHostName("花花");

        System.out.println("name:" + d.getName() + " age:" + d.getAge()
                + "hostName:" + d.getHostName());
        
        System.out.println(d.toString());
    }
}

// 生物
class Creator {
    private int age;

    public int getAge() {
        return age;
    }

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

    public Creator() {
        super();
        System.out.println("this is Creator's constructor");
    }

}

// 动物类
class Animal extends Creator {
    private String name;

    public String getName() {
        return name;
    }

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

    public Animal() {
        super();
        System.out.println("this is Animal's constructor");
    }

}

// 狗
class Dog extends Animal {
    private String hostName;

    public String getHostName() {
        return hostName;
    }

    public void setHostName(String hostName) {
        this.hostName = hostName;
    }

    public Dog() {
        super();
        System.out.println("this is Dog's constructor");
    }

}

4.4 面向对象特征三:多态性

1.多态性的表现:①方法的重载与重写   ②子类对象的多态性
2.使用的前提:①要有继承关系 ②要有方法的重写
3.格式:Person p = new Man();//向上转型
            // 虚拟方法调用:通过父类的引用指向子类的对象实体,当调用方法时,实际执行的是子类重写父类的方法
        p1.eat();
        p1.walk();
       // p1.entertainment();

    
4.>编译时,认为p是Person类型的,故只能执行Person里才有的结构,即Man里特有的结构不能够调用
  >子类对象的多态性,并不使用于属性。

5.关于向下转型:
  ①向下转型,使用强转符:()
   ②为了保证不报ClassCastException,最好在向下转型前,进行判断: instanceof
    // 若a是A类的实例,那么a也一定是A类的父类的实例。
        if (p1 instanceof Woman) {
            System.out.println("hello!");
            Woman w1 = (Woman) p1;
            w1.shopping();
        }

        if (p1 instanceof Man) {
            Man m1 = (Man) p1;
            m1.entertainment();
        }

 4.5 Object类

1.java.lang.Object 类,是所有类的根父类!

2.Object类仅有一个空参的构造器  public Object(){  }

3.关于方法:
  ① equals(Object obj)

  public boolean equals(Object obj) {
        return (this == obj);
   }

// ==
// 1.基本数据类型:根据基本数据类型的值判断是否相等。相等返回true,反之返回false
// 注:两端数据类型可以不同,在不同的情况下,也可以返回true。
// 2.引用数据类型:比较引用类型变量的地址值是否相等。

//equals():
>①只能处理引用类型变量②在Object类,发现equals()仍然比较的两个引用变量的地址值是否相等
>像String 包装类 File类 Date类这些重写Object类的equals()方法,比较是两个对象的
//"实体内容"是否完全相同。
>若我们自定义一个类,希望比较两个对象的属性值都相同的情况下返回true的话,就需要重写Object类的
 equals(Object obj)方法

② toString()方法
当我们输出一个对象的引用时,会调用toString()方法。
1.public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
当我们没有重写Object类的toString()方法时,打印的就是对象所在的类,以及对象实体在堆空间的位置
2.一般我们需要重写Object类的toString()方法,将此对象的各个属性值返回。
3.像String类、Date、File类、包装类都重写了toString()方法。

 4.6 包装类

基本数据类型由于不是类,不能够使用java类库里提供的大量的方法。所有在设计上,
我们让每一个基本数据类型都对应一个类,同时数据存储的范围还不变。此时相当于
基本数据类型就具有了类的特点。这些类即为包装类(wrapper 或封装类)


我们需要掌握的:
基本数据类型、包装类、String类之间的转换!


简易版:
1.基本数据类型与对应的包装类有自动装箱、自动拆箱
如:int i = 10;
      Integer i1 = i;//自动装箱
      int j = i1;//自动拆箱
2.基本数据类型、包装类---->String类:调用String类的重载的valueOf(Xxx xx);
  String类---->基本数据类型、包装类:调用相应的包装的parseXxx(String str);
  注意:String str = "123";
            int i = (int)str;是
错误的转法。

 5 面向对象(三)

5.1 static关键字

1.static:静态的,可以用来修饰属性、方法、*代码块(或初始化块)、*内部类

2.
static修饰属性(类变量):
 * 1.由类创建的所有的对象,都共用这一个属性
 * 2.当其中一个对象对此属性进行修改,会导致其他对象对此属性的一个调用。vs 实例变量(非static修饰的属性,各个对象各自拥有一套副本)
 * 3.类变量随着类的加载而加载的,而且独一份
 * 4.静态的变量可以直接通过“类.类变量”的形式来调用
 * 5.类变量的加载是要早于对象。所以当有对象以后,可以“对象.类变量”使用。但是"类.实例变量"是不行的。
 * 6.类变量存在于静态域中。
 * 
 * static修饰方法(类方法):
 * 1.随着类的加载而加载,在内存中也是独一份
 * 2.可以直接通过“类.类方法”的方式调用
 * 3.内部可以调用静态的属性或静态的方法,而不能调用非静态的属性或方法。反之,非静态的方法是可以调用静态的属性或静态的方法
 *     >静态的方法内是不可以有this或super关键字的!
 * 注:静态的结构(static的属性、方法、代码块、内部类)的生命周期要早于非静态的结构,同时被回收也要晚于非静态的结构


public class TestCircle {
    public static void main(String[] args) {
        Circle c1 = new Circle();
        Circle c2 = new Circle(2.3);
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(Circle.getTotal());
    }
}

class Circle{
    private double radius;
    private static String info = "我是一个圆";
    private int id;//编号
    private static int init = 1001;//控制每个对象的id
    private static int total = 0;//记录创建了多少个对象
    
    public Circle(){
        this.id = init++;
        total++;
    }
    public Circle(double radius){
        this.radius = radius;
        this.id = init++;
        total++;
    }
    
    
    
    public double getRadius() {
        return radius;
    }
    public void setRadius(double radius) {
        this.radius = radius;
    }
    public static String getInfo() {
        return info;
    }
    public static void setInfo(String info) {
        Circle.info = info;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public static int getTotal() {
        return total;
    }
    public static void setTotal(int total) {
        Circle.total = total;
    }
    @Override
    public String toString() {
        return "Circle [radius=" + radius + ", id=" + id + "]";
    }
    public static void show(){
        System.out.println(Circle.info);
    }
    
    public void desc(){
        System.out.println(this.info);
    }

}

 5.1.1 单例模式

23种设计模式

单例模式:
解决的问题:如何只让设计的类只能创建一个对象
如何实现:饿汉式  &  懒汉式
//饿汉式1
class Bank{
    //1.私有化构造器
    private Bank(){}
    //2.创建类的对象,同时设置为private的,通过公共的来调用,体现封装性
    //4.要求此对象也为static的
    private static Bank instance = new Bank();
    //3.此公共的方法,必须为static 
    public static Bank getInstance(){
        return instance;    
    }
}

//饿汉式2
class Bank{
    //1.私有化构造器
    private Bank(){}
    //2.创建类的对象,同时设置为private的,通过公共的来调用,体现封装性
    //4.要求此对象也为static的
    private static Bank instance = null;
    static{
        instance  = new Bank();    
    }
    //3.此公共的方法,必须为static 
    public static Bank getInstance(){
        return instance;    
    }
}

//懒汉式
class Bank{
    private Bank(){}
    
    private static Bank instance = null;
    
    public static Bank getInstance(){
        if(instance == null){//可能存在线程安全问题的!
            instance = new Bank();        
        }    
        return instance;
    }
}

 5.2 main()方法


public static void main(String[] args){
     //方法体     
}

//1.main()是一个方法,是主方法,为程序的入口
//2.权限修饰符:public protected 缺省 private ---面向对象的封装性
//3.对于方法来讲:static final abstract 
//4.方法的返回值:void /  具体的返回值类型(基本的数据类型 & 引用数据类型),方法内部一定要有return
//5.方法名:命名的规则:xxxYyyZzz。给方法命名时,要见名之意
//6.形参列表:同一个方法名不同的形参列表的诸多个方法间构成重载。   形参 & 实参---方法的参数传递机制:值传递
//7.方法体:方法定义的是一种功能,具体的实现由方法体操作。

 5.3 代码块

代码块:是类的第4个成员
作用:用来初始化类的属性
分类:只能用static来修饰。

 * 静态代码块:
 * 1.里面可以有输出语句
 * 2.随着类的加载而加载,而且只被加载一次
 * 3.多个静态代码块之间按照顺序结构执行
 * 4.静态代码块的执行要早于非静态代码块的执行。
 * 5.静态的代码块中只能执行静态的结构(类属性,类方法)
 * 
 * 非静态代码块:
 * 1.可以对类的属性(静态的 & 非静态的)进行初始化操作,同时也可以调用本类声明的方法(静态的 & 非静态的)
 * 2.里面可以有输出语句
 * 3.一个类中可以有多个非静态的代码块,多个代码块之间按照顺序结构执行
 * 4.每创建一个类的对象,非静态代码块就加载一次。
 * 5.非静态代码块的执行要早于构造器
 * 
 * 关于属性赋值的操作:
①默认的初始化
②显式的初始化或代码块初始化(此处两个结构按照顺序执行) 
③构造器中;
—————————以上是对象的属性初始化的过程—————————————
④通过方法对对象的相应属性进行修改

 5.4 final关键字

/*
 * final:最终的 ,可以用来修饰类、属性、方法
 * 
 * 1.final修饰类:这个类就不能被继承。如:String类、StringBuffer类、System类
 * 
 * 2.final修饰方法:不能被重写。如:Object类的getClass()
 * 
 * 3.final修饰属性:此属性就是一个常量,一旦初始化后,不可再被赋值。习惯上,常量用大写字符表示。
 * 此常量在哪里赋值:①此常量不能使用默认初始化 ②可以显式的赋值、代码块、构造器。
 * 
 * 变量用static final修饰:全局常量。比如:Math 类的PI
 * 
 * >与finally finalize()区分开
 * 
 */

class D{
    final int I = 12;
    final double PI;
    final String NAME;

    public void m1(){
        System.out.println(I);
//        I = 10;

    }
    {
        PI = 3.14;
    }
    
    public D(){
        NAME = "DD";
    }
    public D(String name){
        this();
        //NAME = name;
    }
}

5.5 抽象:abstract

重点!

abstract:抽象的,可以用来修饰类、方法

 * 1.abstract修饰类:抽象类
 * 1)不可被实例化
 * 2)抽象类有构造器 (凡是类都有构造器)
 * 3)抽象方法所在的类,一定是抽象类。
 * 4)抽象类中可以没有抽象方法。
 * >当我们设计一个类,不需要创建此类的实例时候,就可以考虑将其设置为抽象的,由其子类实现这个类的抽象方法以后,就行实例化
 * 
 * 2.abstract修饰方法:抽象方法
 * 1)格式:没有方法体,包括{}.如:public abstract void eat();
 * 2)抽象方法只保留方法的功能,而具体的执行,交给继承抽象类的子类,由子类重写此抽象方法。
 * 3)若子类继承抽象类,并重写了所有的抽象方法,则此类是一个"实体类",即可以实例化
 * 4)若子类继承抽象类,没有重写所有的抽象方法,意味着此类中仍有抽象方法,则此类必须声明为抽象的!

 5.5.1 模板方法的设计模式

//模板方法设计模式
public class TestTemplate {
    public static void main(String[] args) {
        new SubTemplate().spendTime();
    }
}

abstract class Template {

    public abstract void code();

    public void spendTime() {
        long start = System.currentTimeMillis();

        this.code();

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

class SubTemplate extends Template {
    
    public void code() {
        boolean flag = false;
        for(int i = 2;i <= 10000;i++){
            for(int j = 2;j <= Math.sqrt(i);j++){
                if(i % j == 0){
                    flag = true;
                    break;
                }
            }
            if(!flag){
                System.out.println(i);
            }
            flag = false;
        }
    }
}

 5.6 接口interface

 * 接口(interface)  是与类并行的一个概念
 * 1.接口可以看做是一个特殊的抽象类。是常量与抽象方法的一个集合,不能包含变量、一般的方法。
 * 2.接口是没有构造器的。
 * 3.接口定义的就是一种功能。此功能可以被类所实现(implements)。
 * 比如:class CC extends DD implements AA
 * 4.实现接口的类,必须要重写其中的所有的抽象方法,方可实例化。若没有重写所有的抽象方法,则此类仍为一个抽象类
 * 5.类可以实现多个接口。----java 中的类的继承是单继承的
 * 6.接口与接口之间也是继承的关系,而且可以实现多继承
 * >5,6描述的是java中的继承的特点。
 * 7.接口与具体的实现类之间也存在多态性

 * 8.面向接口编程的思想:

 5.6.1 工厂方法的设计模式

//接口的应用:工厂方法的设计模式
public class TestFactoryMethod {
    public static void main(String[] args) {
        IWorkFactory i = new StudentWorkFactory();
        i.getWork().doWork();
        
        IWorkFactory i1 = new TeacherWorkFactory();
        i1.getWork().doWork();
    }
}

interface IWorkFactory{
    Work getWork();
}
class StudentWorkFactory implements IWorkFactory{

    @Override
    public Work getWork() {
        return new StudentWork();
    }
    
}
class TeacherWorkFactory implements IWorkFactory{

    @Override
    public Work getWork() {
        return new TeacherWork();
    }
    
}

interface Work{
    void doWork();
}

class StudentWork implements Work{

    @Override
    public void doWork() {
        System.out.println("学生写作业");
    }
    
}
class TeacherWork implements Work{

    @Override
    public void doWork() {
        System.out.println("老师批改作业");
    }
    
}

 5.6.2 代理模式

//接口的应用:代理模式(静态代理)
public class TestProxy {
    public static void main(String[] args) {
        Object obj = new ProxyObject();
        obj.action();
    }
}

interface Object{
    void action();
}
//代理类
class ProxyObject implements Object{
    Object obj;
    
    public ProxyObject(){
        System.out.println("代理类创建成功");
        obj = new ObjctImpl();
    }
    
    public void action(){
        System.out.println("代理类开始执行");
        obj.action();
        System.out.println("代理类执行结束");
    }
}
//被代理类
class ObjctImpl implements Object{

    @Override
    public void action() {
        System.out.println("=====被代理类开始执行======");
        System.out.println("=====具体的操作======");
        System.out.println("=====被代理类执行完毕======");
        
    }
    
}

 5.7 内部类

 * 类的第5个成员:内部类
 * 1.相当于说,我们可以在类的内部再定义类。外面的类:外部类。里面定义的类:内部类
 * 2.内部类的分类:成员内部类(声明在类内部且方法外的)  vs 局部内部类(声明在类的方法里)
 * 3.成员内部类:
 *         3.1是外部类的一个成员:①可以有修饰符(4个)②static final ③可以调用外部类的属性、方法
 *             
 *         3.2具体类的特点:①abstract ②还可以在其内部定义属性、方法、构造器
 * 
 * 4.局部内部类:
 * 
 * 5.关于内部类,大家掌握三点:
 *   ①如何创建成员内部类的对象(如:创建Bird类和Dog类的对象)
 *   ②如何区分调用外部类、内部类的变量(尤其是变量重名时)
 *   ③局部内部类的使用 (见TestInnerClass1.java)

 总结

面向对象的三条主线:
1.类及类的成分
2.面向对象的三大特性
3.其他的关键字:this super import package abstract static final interface等

1.java程序是关注于类的设计。类从代码的角度:并列关系!   从执行、设计的角度:关联关系、继承关系、聚合关系
class A{

}

class B{
   A  a = new A();
}
2.类的成分:属性   方法  构造器  代码块  内部类
    2.1 属性:①变量的分类:成员变量(属性 Field)  vs 局部变量(方法的形参、方法内部、代码块内部)
                         基本数据类型(8种,不同的数据类型对应不同的默认初始化值)  vs 引用数据类型(数组、类、接口 默认初始化值为null)
                ②属性的声明格式:修饰符  数据类型  变量名 = 初始化值;//java是强数据类型的语言
            ③对属性的赋值的操作:1.默认初始化 2.显式的初始化  3.代码块的初始化  4.构造器的初始化;5.调用方法、属性进行赋值
        2.2 方法   ①格式:修饰符 (其它的关键字:static/final/abstract)返回值类型 方法名(形参列表){ //方法体 }
                       ②方法的重载(overload)   vs  方法的重写(override overwrite)
                ③ 方法的参数传递机制:值传递(难点)
       2.3构造器   ①构造器的作用:1.创建类的对象  2.初始化对象的成员变量
                        ②构造器也是可以重载的。
      2.4 代码块   主要作用:用来初始化类的成员变量
                         分类:静态的代码块  vs  非静态的代码块
       2.5 内部类   ①分类:成员内部类(static的成员  vs  非static的成员)  vs 局部内部类(方法内部声明的类)
            ②掌握 :①如何创建成员内部类的对象(如:创建Bird类和Dog类的对象)
                   ②如何区分调用外部类、内部类的变量(尤其是变量重名时)
                   ③局部内部类的使用 (见TestInnerClass1.java)
3. 类的初始化(创建类的对象)
    3.1 如何创建类的对象。如: Person p = new Person();  Date d = new Date();
    3.2 内存解析:
           ①栈:局部变量、对象的引用名、数组的引用名
              堆:new 出来的“东西”
              方法区:(字符串常量池)
              静态域:存放类中静态的变量
           ②如何理解创建的对象在内存中加载的过程(理解)
    3.3 子类对象实例化的全过程: SubClass sc = new SubClass();
4.面向对象的三大特性:
   4.1 封装性:
    ① 通过私有化类的成员变量,通过公共的getter和setter方法来调用和修改
        ② 还可以对类的其他结构进行“封装”
        ③ 权限修饰符:public protected 缺省 private
   4.2 继承性:
    通过让一个类A继承另一个类B,就可以获取类B中的结构(主要的:属性、方法、构造器)。子类:类A  父类:类B
        >java中的类的继承性:单继承的。
   4.3 多态性:
        ①体现:方法的重载与重写  ; 子类对象的多态性  Person p = new Student();
        ②子类对象多态性的使用:虚拟方法调用。
        ③向上转型  向下转型 Student s = (Student)p;  //建议在向下转型之前: if ( p instanceof Student)避免出现ClassCastException的异常

5.其它关键字:
    5.1 this:修饰属性、方法、构造器  。表示:当前对象或当前正在创建的对象
    5.2 super:修饰属性、方法、构造器。显式的调用父类的相应的结构,尤其是子父类有重名的方法、属性
    5.3 static :  修饰属性、方法、代码块、内部类。随着类的加载而加载!
    5.4 final:修饰类、属性、方法。表示“最终的”
    5.5 abstract : 修饰类、方法
    5.6  interface:表示是一个接口,(接口是与类并列的一个结构)。类与接口之间同时“implements”发生关系。
    5.7 package import 。。。
>abstract不能修饰属性、构造器、不能与final static private共用。

        

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值