bingmous

欢迎交流,不吝赐教~

导航

《Java从入门到精通》学习笔记(详细)

目录

03 Java语言基础

基本数据类型

变量与常量

运算符

类型转换

代码规范

标识符命名规范

常用输入输出

04 流程控制

条件语句

循环语句

循环控制

练习

05 字符串

创建字符串

连接字符串

获取字符串信息 

字符串操作

格式化字符串

使用正则表达式

字符串生成器

练习

06 数组

数组的创建与使用

数组的基本操作

数组排序算法

07 类和对象

类的构造方法

静态变量、常量和方法

类的主方法

对象

08 包装类

java.lang包

Integer

Boolean

Byte

Character

Double

Number

09 数字处理类

数字格式化

数学运算

随机数

大数字运算

10 接口、继承与多态

类的继承

Object类

对象类型的转换

方法的重载

多态

抽象类和接口

11 类的高级特性

Java类包

final变量

final方法

final类

内部类

12 异常处理

异常

Java常见异常

自定义异常

运行时异常

13 Swing程序设计

常用Swing组件

常用窗体

标签组件与图标

常用的布局管理器

常用面板

按钮组件

列表组件

文本组件

常用监听事件

14 集合类

Collection接口

List集合

Set集合

Map集合

15 I/O(输入/输出)

输入/输出流

File类

文件输入/输出流

带缓存的输入/输出流

数据输入/输出流

ZIP压缩输入/输出流

16 反射

Class类与反射

访问构造方法

访问成员变量

访问方法

使用Annotation功能

定义Annotation类型

访问Annotation信息

17 枚举类型与泛型

枚举类型

泛型

18 多线程

实现线程的两种方式

操作线程的方法

线程的优先级

线程同步

19 网络通信

网络程序设计基础

TCP程序设计基础

UDP程序设计基础

20 数据库操作

JDBC常用的类和接口

数据库操作


03 Java语言基础

基本数据类型

Java中共有8中数据类型,整型(byte,short,int,long)、浮点型(float,double)、字符型、布尔型。

  • 进制:十进制不能以0开头,八进制以0开头,十六进制以0X或0x开头。
  • 整型:使用long类型时要在后面加L或l防止精度缺失(溢出)。Java默认整数为int类型

  • 浮点型:使用float类型时要在后面加F或f否则会出错。Java默认小数为double类型

为避免4.35*100=4.34999999的问题,使用Math.round()进行四舍五入。

为避免2.0-1.9 == 0.1不相等,使用Math.abs(),如果两个数之间的绝对值小于1e-6,判定为0,即认为相等

  • 字符类型:Java使用unicode编码,字符编码从0x0000-0xffff。占两个字节。
    • char型
    • 转义

  • 布尔类型:true、false

变量与常量

  • 标识符:由字母、下划线、美元符号、数字组成,并且第一个字符不能是数字。
  • 关键字:

  • 变量:
    • 必须是一个有效的标识符;不能使用关键字;不重复;选择有意义的变量名。
    • 局部变量屏蔽静态变量
  • 常量:final 数据类型 变量名[=值]。
    • final变量属于成员变量(类成员变量)时必须在定义时赋值。
    • 属于方法内的常量时可以先定义,后赋值。
  • 变量的范围:
    • 成员变量分为静态变量(加static int a;可以跨类,使用类.变量名引用)和实例变量(一般的int a)。
    • 局部变量(方法中定义的变量)屏蔽成员变量(类中定义的变量)

运算符

  • 赋值运算符:=
  • 算术运算符:

  • 自增自减运算符:++a,a++,--a,a--。
    • 先改变值还是先使用值的区别。
    • 单目运算符,不能放在括号外。
  • 比较运算符:
    • 不能串联使用

  • 逻辑运算符:
    • &&,||:操作数必须是boolean类型,短路
    • 位逻辑运算符也可以进行逻辑运算:&、|、^

  • 位运算符:
    • 位逻辑运算符:按位与&、按位或|、按位取反~、按位异或^;
    • 位移运算符:左移<<、右移>>、无符号右移>>>
    • 技巧:右移移位相当于除以2,左移移位相当于除以2。
  • 三元运算符:条件表达式? 值1:值2; 条件表达式为true整个表达式取值1,否则取值2。
    • 是有返回值的,必须使用。
  • 运算符优先级

类型转换

  • 隐式类型转换:byte < short < int < long < float < double。char型也可以转换为int及更高级类型。转换为byte、short有可能溢出,因为byte只有一个字节、short有两个字节,但是超过32767的数就会为负数,char是unicode编码为两个字节。
  • 显式类型转换:
    • 除了boolean之外,其他基本类型之间的转换都能以显式类型转换的方法达到。int a = (int)1.2;
    • 把一个值赋给byte、short、int、long型变量时,不能超出变量的取值范围,否则会报错,必须使用强制类型转换。
    • 强制类型转换会造成数据失准、小数点丢失、溢出。

代码规范

  • 代码注释:单行注释//、多行注释/*...*/、文档注释/**...*/
  • 注意:多行注释不可嵌套多行注释

标识符命名规范

  • 类名:通常使用名词,所有单词首字母大写
  • 方法名:通常使用动词,首单词字母小写,后续单词首字母大写
  • 变量:首单词字母小写,后续单词首字母大写
  • 常量:所有字母均大写

常用输入输出

package Number;

import java.io.IOException;
import java.util.Scanner;

public class ex_InputOutput {
    public static void main(String[] args)
    throws IOException {
        //输入
        //新建一个扫描器,System.in读取标准输入设备数据(一般是键盘),
        //其类型为InputStream
        Scanner sc = new Scanner(System.in);
//        String str = sc.next();  //接收一个字符串
//        System.out.println("String : " + str);
//        String str2 = sc.nextLine();  //接收一行
//        System.out.println(str2);

//        char c = (char) System.in.read();  //接收一个字符,对于多个字符只接收一个,需抛出异常
//        System.out.println("char :" + c);

//        byte by = sc.nextByte();  //1字节,8位,接收一个byte的数据,多就会报错(汉字)
//        System.out.println("byte :" + by);

//        short sh = sc.nextShort();  //2字节,16位,-32768-32767
//        System.out.println("short :" + sh);

//        int x = sc.nextInt();  //4字节,32位,-2147483648-2147483647
//        System.out.println("int :" + x);

//        long l = sc.nextLong();  //8字节,64位
//        float f = sc.nextFloat();  //4字节,32位
//        double d = sc.nextDouble();  //8字节,64位

//        boolean bo = sc.nextBoolean();  //只接收true/false
//        System.out.println("boolean :" + bo);

    }
}

04 流程控制

条件语句

  • if
  • if else
  • if else if
    • 表达式的值必须是boolean类型
    • 如果没有大括号else与最近的if匹配
    • 单个语句序列可以省略大括号,但最好不要省略,增加可读性
    • 如果没有语句可以使用
    • else和else if 不能单独使用,必须结合if使用
  • switch case:
    • 表达式的值必须是整型、字符型或字符串型(JDK1.7之后)、枚举
    • case 之间的值必须不相同
    • 每一种情况遇到break结束,没有break继续向下执行直到遇到break
    • 没有符合情况的执行default后面的,default可选
package Number;

public class ex4_5_Getifelse {
    public static void main(String[] args) {
        int math = 95;
        int english = 56;
        if (math > 60) {
            System.out.println("math is passed");
        } else {
            System.out.println("math is not passed");
        }
        if (english > 60) {
            System.out.println("english is passed");
        } else {
            System.out.println("english is not passed");
        }

        int x = 20;
        if (x > 30) {
            System.out.println("x>30");
        } else if (x > 10) {
            System.out.println("x>10");
        } else if (x > 0) {
            System.out.println("x>0");
        }
    }
}

 

package Number;

public class GetSwitch {
    public static void main(String[] args) {
        int week = 2;
        switch (week) {
            case 1:
                System.out.println("1");
                break;
            case 2:
                System.out.println("2");
                break;
            case 3:
                System.out.println("3");
                break;
            default:
                System.out.println("sorry");
        }
    }
}

循环语句

  • while() {}
    • 表达式不允许为空
    • 表达式不允许为false
    • 循环体中要有改变条件的语句
  • do {} while ();
    • 结尾处多了一个分号
    • while先判断再执行,do while 先执行再判断
  • for(;;)
    • 表达式1初始化表达式,表达式2是值为boolean的表达式,表达式3为循环后的操作表达式修整变量,改变循环条件
    • 有时使用for(;;)实现无限循环,使用break跳出循环
  • for(type x:遍历对象obj) {引用了x的java语句}
package Number;

public class ex4_10_GetSum {
    public static void main(String[] args) {
        int x = 1;
        int sum = 0;
        while (x <= 10) {
            sum = sum + x;
            x++;
        }
        System.out.println("sum=" + sum);

        int a = 100;  // while -- do...while
        while (a == 60) {
            System.out.println("ok1");
            a--;
        }
        int b = 100;
        do {
            System.out.println("ok2");
            b--;
        } while (b == 60);
    }
}
package Number;

public class ex_4_12_Circulate {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 2; i <= 100; i += 2) {
            sum = sum + i;
        }
        System.out.println("sum:" + sum);

        // foreach
        int arr[] = {7, 10, 1};
        for (int x : arr) {
            System.out.println(x);
        }
    }
}

循环控制

  • break 结束循环
    • break 标签;结束指定的循环体。例如结束外层循环。
  • continue 结束本次循环
    • continue 标签; 结束本次指定的循环体。例如结束这一次的外层循环,继续下一次的外层循环。
package Number;

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

        // for
        for (int i = 0; i <= 100; i++) {
            System.out.println(i);
            if (i == 6) {
                break;
            }
        }
        System.out.println("----end----");

        //使用标签功能,结束外层循环
        Loop:
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 6; j++) {
                if (j == 4) {
                    break Loop;
                }
                System.out.println("i=" + i + " j=" + j);

            }
        }

        //continue
        for (int i = 1; i < 20; i++) {
            if (i % 2 == 0) {
                continue;
            }
            System.out.println(i);
        }
    }
}

练习

  • 打印菱形
  • 使用while计算1 + 1/2! + 1/3! + ... + 1/20!
package Number;

public class ex4_practice_2 {
    public static void main(String[] args) {
        //应用for循环打印菱形
        int lineCount = 7;  //菱形的总行数
        int maxLineNum = lineCount / 2 + 1;  //菱形的上半部分行数
        for (int i = 1; i <= maxLineNum; i++) {
            for (int space = maxLineNum - i; space >= 1; space--) {  //空格数与行数相反
                System.out.print(" ");
            }
            for (int star = 1; star <= i * 2 - 1; star++) {
                System.out.print("*");
            }
            System.out.println();
        }

        //菱形下半部分
        for (int i = maxLineNum - 1; i >= 1; i--) {
            for (int space = 1; space <= maxLineNum - i; space++) {
                System.out.print(" ");
            }
            for (int star = 1; star <= i * 2 - 1; star++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
package Number;

public class ex4_practice_3 {
    public static void main(String[] args) {
        int t = 20;
        double sum = 0;
        for (int i = 1; i <= t; i++) {
            int multi = 1;
            
            for (int j = 1; j <= i; j++) {
                multi = multi * j;
            }
            sum = sum + 1.0 / multi;
        }
        System.out.println("for--sum: " + sum);

        int i = 1;
        int m = 1;
        double sum2 = 0;
        while (i <= t) {
            m *= i;
            sum2 += 1.0/m;
            i++;
        }
        System.out.println("while--sum: " + sum2);

    }
}

05 字符串

创建字符串

  • 引用字符串常量
    • String a; a = "student";
    • 使用相同的字符串常量赋值给不同的变量,他们的地址是一样的(a==b),程序会在栈中查找存不存在常量,存在直接将地址赋给新的变量而不是重新在堆中创建对象
  • 利用构造方法
    • String a = new String("student")
    • String b = new String(a)
  • 利用字符数组实例化
    • String a = new String(char a[])
    • String a = new String(char a[], int offset, int length)
  • 利用字节数组实例化
    • String a = new String(byte a[])

连接字符串

  • 连接多个字符串
    • +
    • += 变量名字很长时,可读性高
  • 连接其他类型
    • 只有+运算符有一个操作数是字符串,编译器就会将另一个操作数转换为字符串(自动调用toString()方法)

获取字符串信息 

  • 获取字符串长度
    • str.length()
    • str.lastIndexOf(""); 空字符串在字符串里的索引等于字符串的长度
  • 字符串查找
    • str.indexOf(String s); 返回s在指定字符串中首次出现的索引位置,没有检索到返回-1
    • str.indexOf(String s, int fromIndex);
    • str.lastIndexOf(String s); 返回s搜索的字符串最后一次出现的位置,没有检索到返回-1
    • str.lastIndexOf(String s, int fromIndex);
  • 获取指定索引位置的字符
    • str.charAt(int index)

字符串操作

  • 获取子字符串
    • str.substring(int beginIndex); 返回从索引位置到结尾的子串
    • str.substring(int beginIndex, int endIndex); 返回从索引开始到结束的子串,长度为endIndex - beginIndex,不包括最后一个索引所指的字符
  • 去除字符串前后空格
    • str.trim();
  • 字符串替换
    • str.replace(char oldChar, char newChar); 替换字符或字符串,没有出现返回原字符串
    • str.replaceAll("\\s", ""); 去掉所有空格,前一个参数是正则表达式,后一个参数是替换的内容
  • 判断字符串的开始与结尾
    • str.startsWith(String prefix); 返回值为boolean类型
    • str.endsWith(String suffix);
  • 判断字符串相等
    • str.equals(String otherstr); ,返回boolean值
    • str.equalsIgnoreCase(String otherstr); 忽略大小写
  • 按字典顺序比较字符串
    • str.compareTo(String otherstr); 逐个比较字符串中的字符,如果str在otherstr之后,在之前返回一个负整数,在之后返回一个正整数,返回值为之前或之后的第几个位置。相等返回0
    • str.compareToIgnoreCase(String otherstr);
  • 字母大小写转换
    • str.toLowerCase();
    • str.toUpperCase(); 数字或非字符不受影响
  • 字符串分割
    • str.split(String sign); sign是分隔符,使用“|”定义多个分隔符,也可以使用正则表达式
    • str.split(String sign, int limit); limit限定分割次数,次数小于等于0、大于最大分割数全分割
    • split会匹配正则表达式,.表示匹配任意字符,所以需要使用\转义,而\本身也需要转义所以是\\.

格式化字符串

String类的静态方法formate()用于创建格式化的字符串。

  • 日期和时间字符串格式化
    • 需要导入import java.util.Date
    • Date date = new Date(); String s = String.formate(String form, date); s是格式化后的字符串
  • 常规类型格式化

 

使用正则表达式

  • 正则表达式元字符,这个元字符表示特定的字符集合
  • 使用方括号[]括起来表示一个元字符,该元字符可以代表元字符中的任何一个字符[abc]4代表a4、b4、c4
    • [^abc] 表示除abc之外的字符,^表示相反
    • [a-z] 表示a-z中任何一个字母,-表示范围
    • [a-e[g-z]] []中加[]表示并运算
    • [a-o&&[def]] &&表示交运算
    • [a-d&&[^bc]] &&结合^表示差运算
  • |表示或、&&表示交、[]表示其中的任意一个、^表示相反
  • 限定修饰符限定元字符出现次数
  • str.matchs(String regex); 返回boolean

字符串生成器

使用String创建的字符串其长度是固定的,使用+进行连接多个字符串时,会产生一个新的String实例,在内存中创建新的字符串对象,如果重复的对字符串进行修改,会极大的增加系统开销。JDK5新增了可变字符序列(String-Builder)类,大大的提高了频繁增加字符串的效率。

  • StringBuffer与StringBuider比较:
    • 提供兼容的API,但是StringBuider不保证线程同步,缺点是只能在单线程中使用,优点是不会给线程加锁

  • StringBuffer、StringBuider、String可以相互转换
    • String转换到StringBuffer或StringBuider使用构造方法new StringBuider(String)或new StringBuider(String)
    • StringBuffer、StringBuider转换到String使用toString()方法
    • StringBuffer与StringBuider方法相互转换先使用toString()方法转换为String再使用构造方法
  •  
  • StringBuilder builder = new StringBuider();
  • StringBuilder builder = new StringBuider("123");
  • StringBuilder builder = new StringBuider(32); 初始容量为32个字符
    • append(content); 添加内容,返回StringBuider对象,地址
    • insert(int offset, arg); 插入内容,返回StringBuilder对象
    • delete(int start, in end); 删除子串,返回StringBuider对象
    • setCharAt(int index, char ch); 无返回值
    • reverse(); 反序输出;
    • toString(); 返回String对象
    • 与String相似的方法
      • int lengt = builder.length(); 返回字符串长度
      • char chr = builder.charAt(int index); 获取指定索引的字符串
      • int index = builder.indexOf(String str); 获取指定字符串的索引
      • String substr = builder.substring(int beginIndex, int endIndex); 获取子串
      • StringBuider tmp = builder.replace(int beginIndex, int endIndex, String str); 替换指定字符序列(与String不同)
    • 其他类方法查询java.lang.StringBuilder的API说明
    •  
  • StringBuffer buffer = new StringBuffer(); 同StringBuider
package ex5_String;

public class ex5_28_Jerque {
    public static void main(String[] args) {
        String str = "";
        long starTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            str += i;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("time: " + (endTime - starTime));

        StringBuilder builder = new StringBuilder("");
        starTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            builder.append(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("time: " + (endTime - starTime));
    }
}

练习

  • 写手机号的正则表达式
  • 使用字符串生成器追加字符
package ex5_String;

public class ex5_practice_3 {
    public static void main(String[] args) {
        String text = "12345678912";
        String regex = "1\\d{10}";
        System.out.println(text + " is " + text.matches(regex) + " phone number");

        StringBuilder builder = new StringBuilder("");
        for (int i = 1; i<=10; i++) {
            builder.append(i);
        }
        System.out.println(builder.toString());
    }
}

06 数组

数组的创建与使用

  • 一维数组创建
    • int arr[]; arr = new int[10]; 先声明,再用new分配内存
    • int[] arr = new int[10]; 声明时直接分配内存
    • int arr[]; arr = new int[]{1, 2, 3}; 初始化
    • int[] arr = {1, 2, 3};
  • 二维数组创建
    • int arr[][]; arr = new int[2][3]; 先声明,在分配内存
    • int[][] arr = new int[2][3]; 声明时直接分配内存
    • int[][] arr = new int[2][]; arr[0] = new int[2]; arr[1] = new int[3]; 分别为每一维分配内存
    • int[][] arr = {{1, 2},{3, 4}}; 初始化

数组的基本操作

  • 获取数组长度

    • arr.length; 数组属性length
  • 填充替换数组元素
    • Arrays.fill(int[] a, int value); 用值填充所有数组值
    • Arrays.fill(int[] a, int fromIndex, int toIndex, int value); 填充指定索引范围的值,包括fromIndex,不包括toIndex
      • 索引超出范围会报出异常
  • 排序
    • Arrays.sort(arr); 升序排序
  • 复制数组
    • Arrays.copyOf(arr, int newlength); 复制数组,指定新数组的长度
    • Arrays.copyOfRange(arr, int fromIndex, int toIndex); 复制指定范围的数组到新数组,toIndex可大于arr的长度
    • arr.clone(); 克隆数组
  • 数组查询
    • Arrays.binarySearch(Object, Object key); 在数组中查找key的位置,存在返回索引,不存在返回插入点,返回第一个大于key的元素的负索引{4, 25, 10},若查找8,则返回10的负索引,{4, 8, 10, 25} 中10的索引是2,返回-2
    • Arrays.binarySearch(Object, int fromIndex, int toIndex, Object key);
      • 返回值没找到时总是返回插入点的负索引,保证了找到时返回值大于等于0
      • 索引超出范围会报异常

数组排序算法

  • 冒泡排序
    • 相邻元素之间比较,并交换。
    • 数组长度为len,则外循环控制轮数为从len-1次,内循环控制每一轮的索引从1到新一轮的最后一个元素。轮数增加1,内循环最大索引少1。
  • 直接选择排序
    • 直接比较,不交换,仅记录索引,每次找出最大值后再进行交换。
    • 循环类似冒泡排序,只是比较后更新最小或最大值所在的索引,在内循环结束后直接将最小或最大值与新一轮的最后一个索引进行交换值,最后一个索引为最小或最大值。
  • 反转排序
package ex6_array;

public class ex6_20_Sort {
    int cmpNum = 0;
    int chgNum = 0;

    public static void main(String[] args) {
        int[] arrOri = {63, 4, 24, 1, 3, 15};
        int[] arr = arrOri.clone();
        int[] arr2 = arrOri.clone();
        int[] arr3 = arrOri.clone();
        ex6_20_Sort sorter = new ex6_20_Sort();  //创建排序类的对象
        sorter.bubbleSort(arr);  //调用冒泡排序方法将数组排序
        sorter.selectSort(arr2);  //调用直接选择排序方法将数组排序
        sorter.reverseSort(arr3);  //反转排序

    }

    /**
     * 冒泡排序
     *
     * @param array 要排序的数组
     */
    public void bubbleSort(int[] array) {
        cmpNum = 0;
        chgNum = 0;
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 1; j < array.length - i; j++) {
                if (array[j - 1] > array[j]) {
                    int tmp = array[j - 1];
                    array[j - 1] = array[j];
                    array[j] = tmp;
                    chgNum++;
                }
                cmpNum++;
            }
        }
        showArray(array);
        System.out.println("bubbleSort--" + "cmpNum: " + cmpNum + " chgNum: " + chgNum);
    }

    /**
     * 直接选择排序
     *
     * @param array 要排序的数组
     */

    public void selectSort(int[] array) {
        cmpNum = 0;
        chgNum = 0;
        int index;
        for (int i = 0; i < array.length - 1; i++) {
            index = 0;
            for (int j = 1; j < array.length - i; j++) {
                if (array[j] > array[index]) {
                    index = j;
                    chgNum++;
                }
                int tmp = array[array.length - i - 1];
                array[array.length - i - 1] = array[index];
                array[index] = tmp;
                cmpNum++;
            }
        }
        showArray(array);
        System.out.println("selectSort--" + "cmpNum: " + cmpNum + " chgNum: " + chgNum);
    }

    /**
     * 反转排序
     *
     * @param array 要排序的数组
     */
    public void reverseSort(int[] array) {
        System.out.print("before reverse: ");
        showArray(array);
        int tmp;
        int len = array.length;
        for (int i = 0; i < len / 2; i++) {
            tmp = array[i];
            array[i] = array[len - i - 1];
            array[len - i - 1] = tmp;
        }
        System.out.print("after reverse:");
        showArray(array);
    }

    /**
     * 显示数组中的所有元素
     *
     * @param array 要显示的数组
     */
    public void showArray(int[] array) {
        for (int x : array) {
            System.out.print(x + "-");
        }
        System.out.println();
    }

}

07 类和对象

  • 面向对象程序设计:封装性、继承性、多态性
  • 成员变量
  • 成员方法
  • 权限修饰符
    • 默认为包内可见
    • 类的权限设定约束类成员的权限
    • private,你无法访问:本类中可见,本包的类、其他包的类都不可见
    • protected,继承访问权限:本包的类可见,其他包的类不可见,其他包的类继承后可见
    • public,接口访问权限:本类、本包的类、其他包的类都可见
    • 默认包访问权限:本包的类可见,其他包的类不可见,其他包的类继承后也不可见

  • 局部变量及有效范围
    • 从变量声明开始到变量结束
    • 变量名相同时,局部变量屏蔽作用域外的变量
  • this关键字
    • this是指向本类对象的指针
    • 在局部变量覆盖成员变量时使用this指针明确引用成员变量

类的构造方法

  • 与类同名
  • 没有构造方法系统自动创建一个不带参数的默认构造方法
  • 如果类中有构造方法,则系统不会自动创建无参数的构造方法
  • this可以调用有参数的构造方法,只能在构造方法的第一句使用

静态变量、常量和方法

  • 归属于类,针对类的而不是对象,对于不同的子类,不同的对象之间,静态变量、静态常量、静态方法使用同一块内存区域
  • 静态数据、静态方法通常是为了提供共享数据或方法,以static声明,同样遵循权限修饰符的约束
  • 静态常量使用final static声明
  • 使用类.静态类成员调用,最好不要使用对象.静态类成员调用,容易混淆静态成员和非静态成员
    • 静态方法不可以直接调用非静态方法
    • 静态方法不可以使用this关键字(因为非静态方法和this都是针对对象的,静态方法是针对类的)
    • 不可以将方法体内的变量声明为static
    • 如果在执行类时,希望先执行某块区域,可以使用static定义一个静态区域 static{//do something} 只会执行一次
  • 程序运行时先运行静态代码块,在运行非静态代码块,在运行构造方法,在运行成员方法(调用才会运行)

类的主方法

  • public static void main(String[] args){}
  • 主方法是静态的,如果直接在主方法中调用其他方法,则该方法必须也是静态的
  • 主方法没有返回值
  • 主方法的形参为数组

对象

  • 对象的创建,new
  • 访问对象的属性和行为,静态变量每个对象都可以改变它的值
  • 对象的引用,创建的对象只是一个地址,表示对对象的引用,也可以不指向某个对象
  • 对象的比较,==比较变量的表面值(即对象的地址),String的equals()方法比较对象指向的值
  • 对象的销毁,定义finalize()方法;System.gc()强制启动垃圾回收

08 包装类

java.lang包

Integer

  • 构造方法
    • Integer(int number);
    • integer(String str); 数值型的String
  • 常量:
    • MAX_VALUE int类型最大值
    • MIN_VALUE int类型最小值
    • SIZE 补码表示的int值的位数
    • TYPE 基本类型int的Class实例
  • 常用方法

Boolean

  • 构造方法
    • Boolean(boolean value);
    • Boolean(String str); 如果String参数不为null,且忽略大小写时等于true,则分配一个表示true的对象,否则分配false
  • 常量
    • TRUE,对应基值为true的Boolean对象
    • FALSE,对应基值为false的Boolean对象
    • TYPE,基本类型为boolean的Class对象
  • 常用方法

Byte

  • 构造方法
    • Byte(byte value);
    • Byte(String str); String参数必须是数值型
  • 常量
    • MIN_VALUE,byte类型可取的最小值
    • MAX_VALUE,byte类型可取的最大值
    • SIZE,二进制补码形式表示的byte值
    • TYPE,表示基本类型byte的Class实例
  • 常用方法

Character

  • 构造方法
    • Character(char value)
  • 常量,Character提供了大量表示特定字符的常量
    • CONNECTOR_PUNCTUATION,返回byte类型值,表示Unicode规范中的常规类别Pc
    • UNASSIGNED,返回byte值,表示Unicode规范中的常规类别Cn
    • TITLECASE_LETTER,返回byte类型值,表示Unicode规范中的常规类别Lt
  • 常用方法

Double

  • 构造方法
    • Double(double value);
    • Double(String str); String参数为数值类型的字符串
  • 常量
    • MAX_EXPONENT,返回int值,表示有限double变量可能具有的最大指数
    • MIN_EXPONENT,返回int值,表示标准化double变量可能具有的最下指数
    • NEGATIVE_INFINITY,返回double值,表示保存double类型的负无穷大的常量
    • POSITIVE_INFINITY,返回double值,表示保存double类型的正无穷大的常量
  • 常用方法

Number

  • 抽象类Number是BigDecimal、BigInteger、Byte、Double、Float、Integer、Long和Short类的父类,Number的子类必须提供将表示的数值转换为byte、double、float、int、long和short的方法
  • Number类的方法分别被Number的各子类所实现,也就是说,在Number类的所有子类中都包含以下方法

09 数字处理类

数字格式化

  • 使用DecimalFormat类
    • 使用实例化时设置格式化模式 DecimalFormat(String pattern);
    • 使用applyPattern()方法对数字进行格式化 x = DecimalFormat(); x.applyPattern(String pattern);
    • 使用特殊的方法进行格式化设置setGroupingSize(), setGroupingUse()...

package ex9_DegitalProcessClass;

import java.text.DecimalFormat;

public class ex_9_1_DecimalFormatSimpleDemo {
    //使用实例化对象时设置格式化模式
    static public void SimpleForm(String pattern, double value) {
        DecimalFormat myFormat = new DecimalFormat(pattern);
        //使用特殊方法对数字进行设置
//        myFormat.setGroupingSize(4);  //设置分组大小
//        myFormat.setGroupingUsed(false);  //是否支持分组
        String output = myFormat.format(value);
        System.out.println(value + " " + pattern + " " + output);
    }

    //使用applyPattern()方法对数字进行格式化
    static public void UseApplyPatternMethodFormat(String pattern, double value) {
        DecimalFormat myFormat = new DecimalFormat();
        myFormat.applyPattern(pattern);
        System.out.println(value + " " + pattern + " " + myFormat.format(value));
    }

    static public void main(String[] args) {
        SimpleForm("###,###.###", 123456.789);  //调用静态方法显示
        SimpleForm("00000000.###kg", 123456.789);  //没有数字的地方显示0
        UseApplyPatternMethodFormat("#.###%", 0.789012);  //显示为百分数,百分数保留三位小数
        UseApplyPatternMethodFormat("#.##", 0.789);  //显示小数点后两位
        UseApplyPatternMethodFormat("0.00\u2030", 0.789);  //显示为千分数,小数点后两位,没有数显示0
    }
}

数学运算

  • Math类,主要包括三角函数方法、指数函数方法、取整函数方法、取最大值最小值平均值函数方法,这些函数都被定义为static形式,在程序中应用比较简便,使用Math.数学方法
  • 三角函数方法,sin(),cos(),tan(),asin(),acos(),atan(),toRadians(),toDegrees();
  • 指数函数方法,exp(),log(),log10(),sqrt(),cbrt(),pow(a, b)
  • 取整函数方法,ceil(),floor(),rint()(返回最近的整数,相等返回偶数),round()(+0.5返回最近的整数)
  • 取最大值最小值绝对值函数方法,max(a, b),min(a, b),abs(a)

随机数

  • Math.random()方法,返回一个大于等于0小于1的随机数
    • 使用这个方法可以产生任意范围的随机数、整数、偶数、奇数、字符((char)('a' + Math.random()*('z' - 'a' +1)),加1使其可能取到z)
  • java.util.Random类
    • Random r = new Random();
    • nextInt()返回一个随机整数
    • nextInt(int n) 返回一个大于等于0小于n的整数
    • nextLong() 返回一个随机长整型值
    • netBoolean() 返回一个随机布尔类型值
    • nextFloat() 返回以个随机浮点型值
    • nextDouble 返回一个随机双精度值
    • nextGaussian() 返回一个概率密度为高斯分布的双精度值

大数字运算

  • java.math.BigInteger 支持任意精度的整数,BigInteger类封装了基本的加减乘除,绝对值,相反数,最大公约数,是否为质数
    • public BigDecimal(double val);
    • public BigDecimal(String val); 常用的两个构造方法
    • public BigInteger(String val); 一种构造方法
    • add(), subtract(), multiply(), divide(), remainder(), divideAndRemainder(), pow(), negate(), shiftLeft(), shiftRight(), and(), or(), compareTo(), equals(), min(), max()
  • java.math.BigDecimal 支持任何精度的定点数
    • add(), subtract(), multiply(), divide(BigDecimal idvisor, int scale, int randingMode); 参数为除数,商的小数点后的位数,近似处理模式

10 接口、继承与多态

类的继承

  • extends关键字标识继承关系
  • super()语句调用父类的构造方法,也可以使用super关键字调用父类的成员方法,但是子类没有权限调用父类private成员方法
  • 重写父类方法时,权限只能由小到大
  • 重写父类方法修改返回值类型只能是父类返回值类型的子类(JDK5)
  • 实例化子类对象时首先要实例化父类对象,无参数构造方法自动调用,有参数的构造方法不能被自动调用(需要传参数)
  • 在继承的机制中,创建一个子类对象将包含一个父类子对象,这个父类子对象与父类创建的对象是一样的,后者来源于外部,前者来源于子类对象的内部(子类对象包含父类子对象)
  • 如果使用finalize()方法对对象进行清理,需要确保子类的finalize()方法的最后一个动作是调用父类的finalize()方法,保证垃圾回收对象占用的内存时,对象的所有部分都能被正常终止
  • static方法必须重写成static方法,final方法不可重写
package ex10_Interface;

class Parent {
    public Parent() {
        System.out.println("调用父类的Parent()构造方法");
    }

    protected void doSomething() {  //父类protected方法
        //do Something
    }

    protected Parent doIt() {  //父类protected方法,返回Partent类型
        return new Parent();
    }
}

class SubParent extends Parent {
    public SubParent() {
        super();  //使用super()调用父类构造方法
        super.doSomething();  //使用super关键字调用父类成员方法
        System.out.println("调用子类的SubParent()构造方法");
    }

    public void doSomethingNew() {  //子类新增方法
        //do something new
    }

    public void doSomething() {  //重写父类方法,修改权限为public
        //do Something
    }

    public Subroutine doIt() {  //重写父类方法,返回SubParent类型
        return new Subroutine();
    }
}

public class Subroutine extends SubParent {
    public Subroutine() {  //自动调用父类构造方法
        System.out.println("调用子类的Subrountine()方法");
    }

    public static void main(String[] args) {
        Subroutine s = new Subroutine();
    }
}

Object类

  • 所有类都继承与java.lang.Object类
    • 任何类都可以重写Object类中的方法 clone(), finalize(), equals(), toString()等,getClass(), notify(), notifyAll(), wait()等方法不能被重写,因为这些方法被定义为final类型
  • getClass()方法获取当前运行的类名(上下文中获得,包含class),返回值是一个Class实例,getClass().getName()返回类名
  • toString()方法将一个对象返回字符串形式
  • equals()方法默认实现的是==比较两个对象的引用,而不是比较内容,要想实现真正的比较对象的内容,需要在自定义类中重写equals()方法
package ex10_Interface;

public class Test {
    public void myMethod(Test t) {
        System.out.println(t);
    }

    public String toString() {  //getClass()自动获取当前类名
        return "在" + getClass().getName() + "中重写toString()方法";
    }


    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t);  //自动调用toString方法
        String s1 = "123";
        String s2 = "123";
        System.out.println(s1.equals(s2));  //返回true
        Test t1 = new Test();
        Test t2 = new Test();
        System.out.println(t1.equals(t2));  //返回false,默认使用==比较引用

        //对象类型转换
        Test t3 = new Test2();
        Test2 t4 = (Test2) t3;  //向下转型
//        System.out.println(t3 instanceof Anything);  //t3不是Anthing的对象,报错

        //多态
        Test2 tt2 = new Test2();
        Test3 tt3 = new Test3();
        t1.myMethod(tt2);  //不同子类的对象调用父类的方法,向上转型
        t1.myMethod(tt3);
        tt2.myMethod(tt2);  //使用父类对象或者子类对象调用都可以
        tt2.myMethod(tt3);

    }
}

class Test2 extends Test {}

class Test3 extends Test {}

class Anything {
}

对象类型的转换

  • 向上转型
    • 子类实例化的对象可以调用父类的方法,传入子类对象,子类对象传入以父类对象为参数的方法为向上转型
    • 子类实例化的对象可以赋值给父类类型的变量(向上转型)
  • 向下转型
    • 将子类实例化的对象赋值给父类类型的变量为向上转型,必须使用强制类型转换(告知编译器这个父类就是某个子类,显式类型转换)将父类类型的变量转换为子类类型,直接赋值会报错
    • 越具体的对象具有的特性越多,越抽象的对象具有的特性越少,在做向下转型时,将特性范围小的对象转换为特性特性范围大的对象肯定会出问题,如果是父类new出来的对象赋值给子类对象,强制类型转换发生ClassCastException异常
  • 使用instanceof判断对象类型
    • 判断对象是否是子类的实例,如果是可以进行向下转型。a instanceof b; 返回布尔类型,不相关的类会报错
    • 也可以判断是否有一个类实现了某个接口

方法的重载

参数个数不同、类型不同、类型顺序不同,不定长参数...(不定长参数作为数组)

多态

子类中的各类似方法的实现只需在父类中实现一次即可,所有继承父类的子类的对象都是父类的子对象,都可以调用父类中以父类对象为参数的方法,子类对象向上转型传入参数。

抽象类和接口

  • 使用abstract关键字定义抽象类和抽象方法,抽象类不可以实例化对象,抽象方法没有方法体,因此抽象方法没有任何意义除非被重写,而承载这个抽象方法的抽象类必须被继承,因此不可能在非抽象类中定义抽象方法,抽象类除了被继承之外没有任何意义
  • 由于继承抽象类必须要实现抽象类中的所有抽象方法,但是有些子类并不需要实现其中的抽象方法,Java不能同时继承多个父类,就有了接口的概念。接口是抽象类的延伸,接口中的所有方法都没有方法体。
  • 使用interface关键字定义接口,可以像类一样使用权限修饰符,public关键字仅限用于与其同名的文件中被定义,接口中的方法必须被定义为public或abstract形式,即使不声明为public,它也是public
    • 接口中定义的任何字段都自动式static和final的
    • 实现接口时使用implements,多个接口使用逗号个卡尼
    • 接口也可以被接口继承,使用extends
package ex10_Interface;

interface drawTest {  //定义接口
    void draw();  //定义方法,省略了public和abstract,其他不被编译器认可
}

interface drawTest2 extends drawTest {
    void draw2(); //接口的继承
}

class A {
}

class B extends A implements drawTest {  //多个接口使用逗号隔开
    public void draw() {  //由于该类实现了接口需要覆盖接口定义的方法
        System.out.println("B类继承A类,实现接口drawTest,B.draw()");
    }
}

class C extends A implements drawTest {
    public void draw() {
        System.out.println("C类继承A类,实现接口drawTest,C.draw()");
    }
}


public class UseInterface {
    public static void main(String[] args) {
        System.out.println("----------------------------");
        drawTest inf = new B();  //接口也可以进行向上转型
        drawTest inf2 = new C();
        inf.draw();
        inf2.draw();
        B b = new B();  //也可以直接使用对象调用成员方法
        b.draw();
    }
}

11 类的高级特性

Java类包

  • 使用package 定义包名,通常使用Internet域名的反序作为包名
  • 使用import导入包,导入包中所有类import com.lzw.*; 但并不会指定这个包的子包的所有类。 或者导入指定类import com.lzw.Math;
  • 同一个包中的类可以不放在同一个位置,只要在CLASSPATH中指定了路径
  • 使用import static导入静态成员(JDK5)

final变量

  • final定义的变量必须在声明时对其赋值,除了修饰基本的数据类型,还可以修饰对象引用,如数组,一旦一个对象使用final修饰,它只能恒定指向一个对象,无法将其改变指向另一个对象。一个既是static有时final的字段是一段不能改变的存储空间
  • final定义的常量使用大写字母命名,中间使用下划线。final定义的变量不可改变,定义的引用不可改变,但是引用的值可以改变
  • final定义的值并不是恒定不变的,例如创建空白final值,在构造方法中对其赋值;对final成员变量赋值为随机值,每次创建新的对象都会有一个随机的final成员变量值(本质上是创建了新的对象,新的对象有了新的final值)
  • final可以修饰的数据
    • final修饰成员变量不可更改
    • final修饰成员变量定义为空白final,在构造方法中为空白final赋值
    • final参数,方法内不可改变参数的值
    • 方法内部设置局部变量为final,不可改变其值
package ex11_AdvancedClass;

import static java.lang.System.out;
import static java.lang.System.setOut;

import java.nio.file.FileAlreadyExistsException;
import java.util.Random;

class Test {
    int i = 0;
}

public class FinalData {
    private static Random rand = new Random(); //实例化一个Random类对象
    private final int VALUE_1 = 9;  //声明一个静态final常量,每个对象都有一份
    private final static int VALUE_2 = 10;  //声明一个final,static常量,归属于类,只有一份
    private final Test test = new Test();  //声明一个final引用
    private Test test2 = new Test();  //声明一个不是final的引用
    private final int[] a = {1, 2, 3, 4};  //声明一个定义为final的数组
    private final int a1 = rand.nextInt(10);
    private final static int a2 = rand.nextInt(10);

    private final int x;  //定义空白final,可在构造方法中初始化

    public FinalData(int y) {
        x = y;  //初始化空白final值
    }


    public static void main(String[] args) {
        FinalData data = new FinalData(1);
//        data.test = Test():  //使用final修饰的引用不可改变指向
        data.test.i = 5;  //不可改变引用,但可以改变引用的值(指向不可变,指向的内容可变)
        data.test2 = new Test();  //没有使用final修饰的引用可以改变指向
//        data.VALUE_1++;  //使用final修饰的变量不可改变
//        data.VALUE_2++;  //使用final修饰的静态变量不可改变,一般使用FinalData.VALUE调用
        data.a[0] = 9;    //不可改变引用,但可以改变引用的值
        out.println(data.a[0]);

        //其中的final x,a1随着新对象的创建都会改变
        //a2为final static,再次创建新对象并不会创建新区域,仍指向初始化时的区域,值也不会改变
        out.println("static a2: " + FinalData.a2);
        FinalData fdata = new FinalData(1);
        out.println("fdata.a1: " + fdata.a1 + " static fdata.a2: " + fdata.a2 + " fdata.x " + fdata.x);
        FinalData fdata2 = new FinalData(2);
        out.println("fdata2.a1: " + fdata2.a1 + " static fdata2.a2: " + fdata.a2 + " fdata2.x " + fdata2.x);
        
    }
}

final方法

  • final定义的方法不能被重写,是因为就是定义为不能重写
  • pravite定义的方法也不能被重写,是因为不能被类以外的区域访问,所以不能重写
    • 区别:pravite定义的方法被隐式的指定为final类型,final类型指定的方法不能被重写但是可以被子类对象向上转型后调用。pravite类型指定的方法被子类对象向上转型后不可调用,因为pravite就是定义了不能被本类以外的区域访问,子类也不可以

final类

final定义的类不允许被继承,类中的所有方法都被隐式的设置为final形式,但是类中的成员变量可以定义为final或非final形式

内部类

  • 成员内部类简介
    • 内部类可以随意使用外部类的成员变量和方法,即使是private
    • 内部类的对象必须绑定到外部类的对象上,内部类的对象与外部类的对象是相互依赖的
    • 实例化内部对象时不能在new操作符之前使用外部类名称实例化内部类对象,而是使用外部类的对象创建其内部类的对象
  • 内部类向上转型为接口
    • 在内部类中实现接口,定义改该内部类为private类型(非内部类不可以定义为private或protected),在外部类中定义一个public方法,该方法返回接口类型,返回对象时内部类对象,这样就实现了内部类对象向上转型为接口。如果某个类继承了外部类
    • 非内部类不能被声明为private或protected
  • 使用this关键字获取内部类和外部类的引用
    • 在内部类中使用this调用内部类对象的引用
    • 使用外部类.this调用外部类对象的引用
  • 局部内部类:在成员方法内定义内部类,如果需要在方法体内使用局部变量,需要设置成final类型,也即是在方法体内定义的内部类只能访问方法中final类型的局部变量,在内部类中,局部变量的值不可以改变。
  • 匿名内部类:创建一个实现接口的匿名类的对象,在匿名内部类定义结束后需要加分号标识,标识创建接口引用表达式的标识。
  • 静态内部类:内部类被声明为static
    • 如果创建静态内部类的对象,不需要外部类的对象
    • 不能从静态内部类的对象中访问非静态外部类的对象
    • 如果在每一个java文件中都设置一个主方法,将出现很多额外代码,而程序本身不需要这些主方法,为了解决这个问题,可以将主方法写入一个静态内部类中,编译后将生成一个名为<外部类$内部类>的独立类和<外部类>,只要使用<外部类$内部类>就可以运行主方法中的内容,完成测试后,需要将所有的class文件打包时只要伸出这个独立类即可

 

//成员内部类
package ex11_AdvancedClass;

public class OuterClass {  //外部类
    innerClass in = new innerClass();  //在外部类实例化内部类的对象引用

    public void outf() {
        in.inf();  //在外部类中调用内部类方法
    }

    public innerClass doit() {  //外部类方法
        //y=4;  //外部类不可以直接访问内部类的成员变量
        in.y = 4;  //可以通过对象访问
        return new innerClass();  //返回内部类的引用
    }

    class innerClass {  //内部类
        int y = 0;  //内部类成员变量

        public innerClass() {
        }  //内部类构造方法

        public void inf() {
        }  //内部类成员方法
    }

    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        //内部类的对象实例化必须在外部类或外部类的非静态方法中实现
        //在其他地方实现需要使用外部类。
        //内部类实例一定要绑定到外部类的实例上,内部类对象依赖于外部类对象
        OuterClass.innerClass in3 = out.doit();  //out对象返回内部类的引用赋值给in
        //不能在new之前使用外部类名称实例化内部类对象,应该使用外部类的对象创建内部类的对象
        OuterClass.innerClass in2 = out.new innerClass();  //out对象创建一个新对象赋值给in2
        System.out.println(in3.y);
        System.out.println(out.in);
        System.out.println(out.in.y);  //out对象调用过doit()方法,把它的y值设置成了4
    }
}

//内部类向上转型为接口
package ex11_AdvancedClass;

interface OutInterface {
    public void f();
}

public class InterfaceInner {
    public static void main(String[] args) {
        OuterClass2 out = new OuterClass2();  //创建外部类对象
        OutInterface outinter = out.doit();  //使用外部类对象调用方法,返回内部类对象向上转型的接口
        outinter.f();  //使用接口调用f()方法
    }
}

class OuterClass2 {
    //定义一个内部类实现OuterInterface接口
    private class InnerClass implements OutInterface {
        public InnerClass(String s) {  //内部类的构造方法
            System.out.println(s);
        }

        public void f() {
            System.out.println("访问内部类的f()方法");
        }
    }

    public OutInterface doit() {  //定义一个外部类方法,返回值类型为OuterInterface接口类型
        return new InnerClass("访问内部类构造方法");  //内部类向上转型为接口
    }
}
//局部内部类
package ex11_AdvancedClass;

interface OutInterface2 {

}

class OuterClass3 {
    public OutInterface2 doit(String x) {
        //在doit()方法内定义一个内部类
        class InnerClass2 implements OutInterface2 {
            InnerClass2(String s) {
                s = x;  //内部类中使用局部变量必须是final变量或者实际上的final变量(不可变)
                System.out.println(s);
            }
        }
        return new InnerClass2("doit");
    }
}

public class Test2 {
    public static void main(String[] args) {
        OuterClass3 out3 = new OuterClass3();
        OutInterface2 outint = out3.doit("hi");

    }
}
//匿名内部类,创建一个实现接口的匿名类的对象
class OuterClass4 {
    public OutInterface2 doit() {  //定义doit()方法
        return new OutInterface2() {  //声明匿名内部类
            private int i = 0;
            public int getValue() {  //实现接口
                return i;
            }
        };  // 在匿名内部类定义结束后需要加分号标识,
    }
}

12 异常处理

异常

try-catch-finally

  • e.getMessage(); 输出错误性质
  • e.toString(); 输出类型和性质
  • e.printStackTrack(); 指出异常的类型、性质、栈层次及出现在程序中的位置
  • catch异常按顺序进行捕捉
package ex12_Exception;


public class Take {
    public static void main(String[] args) {
        try {  //try语句中包含可能出现异常的代码
            String str = "hi";
            System.out.println("str: " + str);
            int age = Integer.parseInt("20L");
            System.out.println("age: " + age);
        } catch (Exception e) {  //获取异常信息
            e.printStackTrace();  //输出异常性质
        }
        System.out.println("program over");
    }
}

Java常见异常

自定义异常

package ex12_Exception;

class MyException extends Exception {  //创建自定义异常,继承Exception
    public MyException(String ErrorMessage) {  //构造方法
        super(ErrorMessage);  //父类构造方法
    }
}

public class Tran {
    static int avg(int number1, int number2) throws MyException {
        if (number1 < 0 || number2 < 0) {  //判断是否满足条件
            throw new MyException("不可使用负数");  //抛出异常信息
        }
        if (number1 > 100 || number2 > 100) {
            throw new MyException("数值太大了");
        }
        return (number1 + number2) / 2;
    }

    public static void main(String[] args) {
        try {  //处理可能出现异常的代码
            int result = avg(102, 150);  //调用avg()方法
            System.out.println(result);
        } catch (MyException e) {
            System.out.println(e);  //输出异常信息
        }

    }
}

运行时异常

Java类库的每个包中都定义了异常,所有这些类都是Throwable的子类,Throwable派生出两个子类,Exception类和Error类

13 Swing程序设计

常用Swing组件

常用窗体

Swing组件的窗体通常与组件和容器相关,所以在JFrame对象创建完成后,需要调用getContentPane()方法将窗体转换为容器,然后在容器中添加组件或设置布局管理器。这个容器用来包含和显示组件,使用Container.add()方法添加组件至容器。使用Container.remove()删除组件。

  • JFrame窗体,继承JFrame类
    • 构造方法
    • JFrame(); 初始化不可见、没有标题
    • JFrame(String title); 不可见、有标题,使用setVisible(true); 可见
    • 窗体关闭类型
    • DO_NOTHING_ON_CLOSE
    • DISPOSE_ON_CLOSE
    • HIDE_ON_CLOSE
    • EXIT_ON_CLOSE
  • JDialog窗体,继承JDialog类
    • 构造方法
    • JDialog();没有标题和父窗体
    • JDialog(Frame f); 指定父窗体的对话框
    • JDialog(Frame f, boolean model); 指定父窗体和指定类型的对话框 
    • JDialog(Frame f, String title); 指定父窗体和标题 
    • JDialog(Frame f, String title, boolean model); 指定父窗体、标题和类型
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class Example1 extends JFrame {  //定义一个类继承JFrame
    public void CreateJFrame(String title) {  //定义一个CreateJFrame方法
        JFrame jf = new JFrame(title);  //实例化一个JFrame对象
        Container container = jf.getContentPane();  //获取一个容器
        JLabel jl = new JLabel("这是一个JFrame窗体");  //创建一个JLabel标签
        jl.setHorizontalAlignment(SwingConstants.CENTER);  //使标签上的文字居中
        container.add(jl);  //将标签添加到容器中
        container.setBackground(Color.white);  //设置容器的背景颜色
        jf.setVisible(true);  //使窗体可见
        jf.setSize(200, 150);  //设置窗体大小
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);  //设置窗体关闭方式
    }

    public static void main(String[] args) {
        new Example1().CreateJFrame("创建一个JFrame窗体");
    }

}
package ex13_Swing;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MyJDialog extends JDialog {  //创建新类继承JDialog
    public MyJDialog() {  //构造方法
        super(new MyFrame(), "第一个JDialog窗体", true);  //实例化一个JDialog对象,指定对话框的父窗体,窗体标题和类型
        Container container = getContentPane();  //创建一个容器
        container.add(new JLabel("这是一个对话框"));  //在容器中添加标签
        setSize(100, 100);
    }

    public static void main(String[] args) {
        new MyJDialog();
    }
}

class MyFrame extends JFrame {

    public MyFrame() {
        Container container = getContentPane();  //创建一个容器
        container.setLayout(null);
        JLabel jl = new JLabel("这是一个JFrame窗体");  //在窗体中设置标签
        jl.setHorizontalAlignment(SwingConstants.CENTER);  //设置标签的位置
        container.add(jl);
        JButton bl = new JButton("弹出对话框");  //定义一个按钮
        bl.setBounds(10, 10, 100, 21);
        bl.addActionListener(new ActionListener() {  //为按钮添加鼠标单击事件
            @Override
            public void actionPerformed(ActionEvent e) {
                new MyJDialog().setVisible(true);
            }
        });
        container.add(bl);  //将按钮添加到容器中
        container.setBackground(Color.white);  //设置容器的背景颜色
        setSize(200, 200);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        setVisible(true);
    }
}

标签组件与图标

  • JLabel构造方法
    • JLabel(); 不带图标和文本
    • JLabel(Icon icon); 带图标
    • JLabel(Icon icon, int aligment); 带图标、水平对齐方式
    • JLabel(String text, int aligment); 带文本、水平对齐方式
    • JLabel(String text, Icon icon, int aligment); 文本、图标、水平对齐方法
  • 使用图片图标 javax.swing.ImageIcon类
    • java.lang.Class类中的getResource()方法可以获取资源文件中的URL路径
//图标的使用
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class DrawIcon implements Icon {  //实现Icon接口
    private int width;  //声明图标的宽
    private int height;  //声明图标的高

    public int getIconWidth() {  //实现getIconWidth()方法
        return this.width;
    }

    public int getIconHeight() {  //实现getIconHeight()方法
        return this.height;
    }

    public DrawIcon(int width, int height) {  //构造方法
        this.width = width;
        this.height = height;
    }

    public void paintIcon(Component arg0, Graphics arg1, int x, int y) {
        arg1.fillOval(x, y, width, height);  //绘制一个图形
    }

    public static void main(String args[]) {
        JFrame jf = new JFrame();  //创建一个JFrame窗口
        Container container = jf.getContentPane();  //创建一个容器
        container.setBackground(Color.white);
        jf.setVisible(true);
        jf.setSize(200,150);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        DrawIcon icon = new DrawIcon(15,15);  //创建标签对象
        //创建一个标签,设置标签上的文字在标签正中间
        JLabel jl = new JLabel("测试12345566661111111", icon, SwingConstants.CENTER);
        container.add(jl);  //添加到容器
    }

}
//图片图标
package ex13_Swing;

import javax.swing.*;
import java.awt.*;
import java.net.URL;

public class MyImageIcon  {
    public static void main(String[] args) {
        JFrame jf = new JFrame("hi");  //创建窗体对象
        Container container = jf.getContentPane();  //创建容器
        JLabel jl = new JLabel("这是一个JFrame窗体", JLabel.CENTER);  //创建标签
        //创建图标图片的url,图片方法class文件夹下
        URL url = MyImageIcon.class.getResource("imageButton.jpg");  
        Icon icon = new ImageIcon(url);  //创建图标
        jl.setIcon(icon);  //为标签设置图标
        jl.setHorizontalAlignment(SwingConstants.CENTER);  //设置文字放在标签中间
        jl.setOpaque(false); //设置标签为不透明
        container.add(jl);  //将标签添加至容器

        jf.setSize(500,500);
        jf.setVisible(true);
        jf.setBackground(Color.blue);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }
}

常用的布局管理器

  • 绝对布局
    • Container.setLayout(null); 取消布局管理器
    • Container.setBound(); 设置每个容器、组件的大小和位置
  • 流布局管理器
    • FlowLayout(int alignment, int horizGap, int vertGap);
    • jf.setLayout(fl)
  • 边界布局管理器(Swing默认)
    • 使用add方法同时设置位置和添加组件
  • 网格布局管理器
    • GridLayout(int rows, int columns, int horizGap, int vertGap); 类似于流布局管理器的使用
//流布局管理器
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class FlowLayoutPosition {
    public static void main(String[] args) {
        JFrame jf = new JFrame("title");  //创建JFrame对象
        Container container = jf.getContentPane();  //创建容器
        //设置jf参数
        jf.setVisible(true);
        jf.setSize(500,500);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //创建流布局管理器对象,设置对齐方式,水平间隔,垂直间隔
        FlowLayout fl = new FlowLayout(FlowLayout.RIGHT, 10,10);
        jf.setLayout(fl);
        for (int i = 0; i < 100; i++) {
            JButton jb = new JButton("button "+ i); //创建JButton对象
            container.add(jb);  //添加到容器
        }
    }
}
//边界布局管理器
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class BorderLayoutPosition {
    public static void main(String[] args) {
        JFrame jf = new JFrame("title");  //创建JFrame对象
        Container container = jf.getContentPane();  //创建容器
        //设置jf参数
        jf.setVisible(true);
        jf.setSize(500, 500);
        jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        BorderLayout bl = new BorderLayout();
        jf.setLayout(bl);  //默认边界布局,可以不写
        //边界布局管理器,上、下、左、右、中
        String[] border = {
                BorderLayout.CENTER,
                BorderLayout.NORTH,
                BorderLayout.SOUTH,
                BorderLayout.WEST,
                BorderLayout.EAST};
        String[] buttonName = {"center", "north", "south", "west", "east"};
        for (int i = 0; i < border.length; i++) {
            JButton jb = new JButton(buttonName[i]);
            container.add(border[i], jb);  //add()方法可以同时添加布局和组件
        }
    }

}

常用面板

面板也是一个Swing容器,继承自java.awt.container类,可以容纳其他组件,但它也必须被添加到其他容器中

  • JPanel面板
  • JScrollPane面板 带滚动条,也是一种容器,但是只能放一个组件,不可以使用布局管理器,如果需要多个组件,需要将多个组件放在JPanel面板上,然后将JPanel面板作为一个整体添加在JScrollPane上

 

//JPanel面板
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class JPanelTest extends JFrame {
    public JPanelTest() {
        Container c = getContentPane();
        //将整个容器设置成2行1列的网格布局
        c.setLayout(new GridLayout(2, 2, 10, 10));
        //初始化面板,设置面板布局
        JPanel jp1 = new JPanel(new GridLayout(1, 4, 10, 10));
        JPanel jp2 = new JPanel(new GridLayout(1, 3, 10, 10));
        JPanel jp3 = new JPanel(new GridLayout(1, 2, 10, 10));
        JPanel jp4 = new JPanel(new GridLayout(1, 5, 10, 10));
        jp1.add(new JButton("1"));  //在面板中添加按钮
        jp1.add(new JButton("2"));
        jp1.add(new JButton("3"));
        jp1.add(new JButton("4"));
        jp2.add(new JButton("21"));
        jp3.add(new JButton("31"));
        jp3.add(new JButton("32"));
        jp4.add(new JButton("41"));
        jp4.add(new JButton("42"));
        jp4.add(new JButton("43"));
        c.add(jp1);  //添加面板到容器
        c.add(jp2);
        c.add(jp3);
        c.add(jp4);
        setVisible(true);
        setSize(500, 500);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new JPanelTest();
    }
}
//JScroll面板
package ex13_Swing;

import javax.swing.*;
import java.awt.*;

public class JScrollPaneTest extends JFrame {
    public JScrollPaneTest() {
        Container c = getContentPane();
        JTextArea jta = new JTextArea(20, 50);  //创建文本区域组件
        JScrollPane jsp = new JScrollPane(jta);  //创建JScrollPane面板对象,将文本区域组件添加上
        c.add(jsp);
        setTitle("带滚动条的文本编辑器");
        setSize(500, 500);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new JScrollPaneTest();
    }
}

按钮组件

  • JButton(String text, Icon icon); 提交按钮组件
  • JRadioButton(); 单选按钮组件,将所有按钮添加到一个面板中,在将面板添加到容器中,所有按钮添加到组才是单选
  • JCheckBox(); 将多个复选框添加到一个面板中,也可以为每个复选框添加监听事件

 

//提交按钮、单选按钮
package ex13_Swing;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;

public class JButtonTest extends JFrame {
    public JButtonTest() {
        Container c = getContentPane();
        URL url = JButtonTest.class.getResource("imageButton.jpg");
        Icon icon = new ImageIcon(url);
        //设置布局3行2列,间隔为5
        setLayout(new GridLayout(5, 1, 5, 5));
        JButton jb1 = new JButton("button1", icon);
        JButton jb2 = new JButton("button2", icon);
        JButton jb3 = new JButton("button3", icon);
        JButton jb4 = new JButton();  //实例化一个没有文字的按钮
        add(jb1);
        add(jb2);
        add(jb3);
        add(jb4);
        jb2.setEnabled(false);  //设置按钮不可用
        jb4.setMaximumSize(new Dimension(10, 10));//设置按钮与图片大小相同
        jb4.setIcon(icon);  //为按钮设置图标
        jb4.setHideActionText(true);
        jb4.setToolTipText("图片按钮");  //设置按钮的提示文字,鼠标指向按钮时
        jb4.setBorderPainted(false);  //设置图标按钮边界不显示
        jb4.addActionListener(new ActionListener() {  //为按钮添加监听事件
            @Override
            public void actionPerformed(ActionEvent e) {
                //弹出确认对话框
                JOptionPane.showMessageDialog(null, "弹出对话框");
            }
        });
        //单选按钮组件
        JRadioButton jrb1 = new JRadioButton();
        JRadioButton jrb2 = new JRadioButton();
        JRadioButton jrb3 = new JRadioButton();
        ButtonGroup bg = new ButtonGroup();  // 添加到组后才是单选按钮
        bg.add(jrb1);
        bg.add(jrb2);
        bg.add(jrb3);
        JPanel jp = new JPanel();  //创建面板
        jp.add(jrb1);
        jp.add(jrb2);
        jp.add(jrb3);
        jp.setBorder(new TitledBorder("TitleBorder"));
        add(jp);


        setVisible(true);
        setSize(500, 500);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new JButtonTest();
    }
}
//复选框组件
package ex13_Swing;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class CheckBoxTest extends JFrame {
    public CheckBoxTest() {
        Container c = getContentPane();  //创建容器
        setLayout(new BorderLayout());
        JPanel jp1 = new JPanel();  //创建面板
        JPanel jp2 = new JPanel();

        JCheckBox jc1 = new JCheckBox("1");  //创建复选框
        JCheckBox jc2 = new JCheckBox("2");
        JTextArea jt = new JTextArea(20, 20);  //创建编辑器
        final JScrollPane jsp1 = new JScrollPane(jt);  //创建滑动面板

        jp1.add(jsp1);  //面板1添加编辑器
        jp2.add(jc1);  //面板2添加复选框
        jp2.add(jc2);  //面板2添加复选框
        jc1.addActionListener(new ActionListener() {  //复选框添加监听事件
            @Override
            public void actionPerformed(ActionEvent e) {
                jt.append("复选框1被选中\n");
            }
        });
        jc2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                jt.append("复选框2被选中\n");
            }
        });

        c.add(jp1, BorderLayout.NORTH);  //添加面板到容器
        c.add(jp2, BorderLayout.SOUTH);


        setSize(500, 500);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {
        new CheckBoxTest();
    }
}

列表组件

  • 下拉列表组件
    • JComboBox(),下拉列表中的项目可以封装为JComBoxModel类型、数组或Vector
  • 列表框组件
    • JList(),数组、Vector类型和ListModel类型

文本组件

  • 文本框组件
    • JTextField()
  • 密码框组件
    • JPasswordField()
  • 文本域组件
    • JTextArea(); 类中存在一个setLineWrap()方法,设置为true表示自动换行

常用监听事件

  • 动作监听事件
    • addActionListener(new jbAction); 在内部类jbAction中实现ActionListener接口,重写actionPerformed方法
    • addActionListener(new ActionListen() {
      public void actionPerformed(ActionEvent arg0) {
      //do something
      }}); 使用匿名内部类实现
  • 交点监听事件
    • addFocusListener(new FocusListener() {
      public void focusLost(FocusEvent arg0) {  //组件失去焦点时调用的方法
      // do something
      }
      public void focusGained(FocusEvent arg0) //组件获取焦点时调用的方法
      });

14 集合类

Collection接口

  • List和Set接口都继承了Collection接口,因此这些方法对List和Set集合是通用的
  • 遍历通过iterator()方法返回迭代器,Iterator的next()返回的是Object

List集合

  • 允许重复,有序,可用索引查询
  • LIst接口继承了Collection接口,包含Collection中的所有方法,还包括两个重要的方法get(int index); 获取指定位置元素set(int index, Object obj); 将集合中指定位置对象修改为指定的对象
  • 实例化List集合
    • List<E> list = new ArrayList<>();  可以根据索引快速随机访问,插入、删除删除速度慢
    • List<E> list = new LinkedList<>();  随机访问速度慢,插入、删除速度快
package ex14_Collection;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class Muster {
    public static void main(String args[]) {
        Collection<String> list = new ArrayList<>();  //实例化集合类对象
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator<String> it = list.iterator();  //创建迭代器
        while (it.hasNext()) {  //判断是否还有下一个元素
            String str = (String) it.next();  //获取集合中的元素
            System.out.println(str);
        }

        //随机获取一个字母,删除索引为2的元素
        List<String> list1 = new ArrayList<>();  //创建集合对象
        list1.add("1");
        list1.add("2");
        list1.add("2");
        int i = (int) (Math.random() * list.size());
        System.out.println("随机访问:" + list1.get(i));
        list1.remove("2"); // 删除对象
        list1.remove(1);  //删除索引位置的对象
        for (int j = 0; j < list1.size(); j++) {  //遍历集合
            System.out.print(list1.get(j) + " ");
        }
    }
}

Set集合

  • 不能重复,无序
  • Set接口常用的实现类有HashSet类和TreeSet类
    • HashSet不保证Set的迭代顺序,允许使用null元素
    • TreeSet实现了Set接口,还实现了java.util.SortedSet接口,TreeSet类实现的Set集合在遍历时按照自然顺序递增排序,也可以按照指定比较器排序
  • 存入TreeSet类实现的Set集合必须实现Comparable接口,该接口中的compareTo(Object o)方法比较此对象与指定对象的顺序

package ex14_Collection;


import java.util.Iterator;
import java.util.TreeSet;

public class UpdateStu implements Comparable<Object> {  //创建类实现Comparable接口
    String name;
    long id;

    public UpdateStu(String name, long id) {
        this.name = name;
        this.id = id;
    }

    public int compareTo(Object o) {
        UpdateStu upstu = (UpdateStu) o;
        int result = id > upstu.id ? 1 : (id == upstu.id ? 0 : -1);
        return result;
    }

    public String getName() {
        return name;
    }

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

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public static void main(String[] args) {
        UpdateStu stu1 = new UpdateStu("李同学", 1011);
        UpdateStu stu2 = new UpdateStu("陈同学", 1021);
        UpdateStu stu3 = new UpdateStu("王同学", 1031);
        UpdateStu stu4 = new UpdateStu("马同学", 1041);
        TreeSet<UpdateStu> tree = new TreeSet<>();
        tree.add(stu1);
        tree.add(stu2);
        tree.add(stu3);
        tree.add(stu4);
        Iterator<UpdateStu> it = tree.iterator();  //Set集合中的所有对象的迭代器
        System.out.println("Set中所有元素:");
        while (it.hasNext()) {  //遍历集合
            UpdateStu stu = (UpdateStu) it.next();
            System.out.println(stu.getId() + " " + stu.getName());
        }

        it = tree.headSet(stu2).iterator();  //截取stu2之前的对象
        System.out.println("截取前面部分的集合:");
        while (it.hasNext()) {  //遍历集合
            UpdateStu stu = (UpdateStu) it.next();
            System.out.println(stu.getId() + " " + stu.getName());
        }

        it = tree.subSet(stu2, stu4).iterator();  //截取stu2-stu4之间的对象
        System.out.println("截取中间部分的集合:");
        while (it.hasNext()) {  //遍历集合
            UpdateStu stu = (UpdateStu) it.next();
            System.out.println(stu.getId() + " " + stu.getName());
        }

    }

}

Map集合

  • Map接口没有继承Collection接口,提供的是key到value的映射,Map中不能包含相同的key
  • Map接口常用的实现类有HashMap和TreeMap
    • HashMap是基于哈希表的Map接口的实现,添加删除映射关系效率高,允许使用null值和null键,不保证映射顺序
    • TreeMap不仅实现了Map接口,还实现了java.util.SortedMap接口,集合中的映射关系有一定的顺序因此键不允许为null,添加删除映射关系效率低
    • keySet()返回Set集合,values返回Collection集合
  • 可以通过HashMap创建Map集合,当需要顺序输出时,再创建一个完成相同映射关系的TreeMap类实例

package ex14_Collection;


import java.util.*;

public class UpdateStu2 {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();  //创建Map实例
        map.put("01", "李同学");  //向集合中添加对象
        map.put("02", "魏同学");
        map.put("03", "魏同学");
        map.put("04", null); //会被打印出来
        Set<String> set = map.keySet();  //构建Map集合中所有key对象的集合
        Iterator<String> it = set.iterator();  //创建集合迭代器
        System.out.println("key集合中的元素:");
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        Collection<String> coll = map.values();  //构建Map集合中所有values值的集合
        it = coll.iterator();
        System.out.println("values集合中的元素");
        while (it.hasNext()) {
            System.out.println(it.next());
        }

    }
}
package ex14_Collection;

public class Emp {
    private String e_id;
    private String e_name;

    public Emp(String e_id, String e_name) {
        this.e_id = e_id;
        this.e_name = e_name;
    }

    public String getE_id() {
        return e_id;
    }

    public String getE_name() {
        return e_name;
    }

    public void setE_id(String e_id) {
        this.e_id = e_id;
    }

    public void setE_name(String e_name) {
        this.e_name = e_name;
    }
}
package ex14_Collection;

import java.util.*;

public class MapText {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        Emp emp = new Emp("351", "张三");  //创建Emp对象
        Emp emp2 = new Emp("512", "李四");
        Emp emp3 = new Emp("853", "王一");
        Emp emp4 = new Emp("125", "赵六");
        Emp emp5 = new Emp("341", "黄七");

        map.put(emp.getE_id(), emp.getE_name());  //添加对象到集合中
        map.put(emp2.getE_id(), emp2.getE_name());
        map.put(emp3.getE_id(), emp3.getE_name());
        map.put(emp4.getE_id(), emp4.getE_name());
        map.put(emp5.getE_id(), emp5.getE_name());

        Set<String> set = map.keySet();  //获取Map集合中的key对象集合
        Iterator<String> it = set.iterator();
        System.out.println("HashMap集合类实现的Map集合,无序:");
        while (it.hasNext()) {
            String str = (String) it.next();
            String name = (String) map.get(str);
            System.out.println(str + " " + name);
        }

        TreeMap<String, String> treeMap = new TreeMap<>();  //创建TreeMap集合对象
        treeMap.putAll(map);
        Iterator<String> iter = treeMap.keySet().iterator();
        System.out.println("TreeMap类实现的Map集合,键值升序:");
        while (iter.hasNext()) {
            String str = (String) iter.next();
            String name = treeMap.get(str);
            System.out.println(str + " " + name);
        }

    }
}

15 I/O(输入/输出)

输入/输出流

java定义了许多专门负责各种方式的输入/输出,都放在java.io包中

输入流:抽象类:InputStream(字节输入流),Reader(字符输入流)

输出流:抽象类:OutputStream(字节输出流),Writer(字符输出流)

  • read(); 从输入流中读取数据的下一个字节。返回0-255范围内的int字节值,没有可用的字节则返回值为-1
  • read(byte[] b); 从输入流中读取一定长度的字节,并以整数的形式返回字节数
  • mark(int readlimit); 在输入流的当前位置放一个标记,readlimit参数告知此输入流在标记位置失效前允许读取的字节数
  • reset(); 将输入指针返回到当前所做的标记处
  • skip(long n); 跳过输入流上的n个字节并返回实际跳过的字节
  • markSupport(); 如果当前流支持mark()/reset()操作就返回true
  • close(); 关闭此输入流并释放与该流关联的所有系统资源
    • 并不是所有的InputStream类都支持InputStream中定义的所有方法,如skip(),mark(),reset()等方法只对某些子类有用 

File类

  • File类是java.io包中唯一代表磁盘文件本身的对象
  • 构造方法有三种
    • new File(String);
    • new File(String father, String child);
    • new File(File f, String child);

package IO;

import java.io.File;

public class FileTest {
    public static void main(String[] args) {
        //使用路径创建File对象
        //File file = new File(String pathname)
//        File file = new File("D:\\word.txt");
        File file = new File("word.txt");

        //使用父路径、子路径创建
        //File file = new File(String father, String child)
//        String fatherPath = "D:\\";
//        String childPath = "word2.txt";
//        File file2 = new File(fatherPath, childPath);

        //使用父路径对象、子路径创建
        //File file = new File(File f, String child)
//        childPath = "word3.txt";
//        File file = new File(file2.getParent(), childPath);
        if (!file.exists()) {
//            file.delete();  //删除文件
//            System.out.println("文件已删除");
        } else {
            try {
                file.createNewFile();  //创建文件
                System.out.println("文件已创建");
                String name = file.getName();  //获取文件名称
                long length = file.length();  //获取文件长度
                boolean hidden = file.isHidden(); //判断是否是隐藏文件
                System.out.println("文件名称:" + name);
                System.out.println("文件长度:" + length);
                System.out.println("是否是隐藏文件:" + hidden);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

文件输入/输出流

  • FileInputStream与FileOutputStream类
    • 不足:这两个类都只提供了对字节或字节数组的读取方法。由于汉字在文件中占两个字节,使用字节流,读取不好会出现乱码,此时采用字符流可避免这种现象
  • FileReader与FileWriter类
//FileInputStream与FileOutputStream类
package IO;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileInputStreamFileOutputStream {
    public static void main(String[] agrs) {
        File file = new File("word.txt");

        //没有文件就创建
        if (file.exists()) {
            System.out.println("存在文件" + file.getPath());
        } else {
            try {
                file.createNewFile();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        //写入字节流
        try {
            //创建FileOutputStream对象
            FileOutputStream outputStream = new FileOutputStream(file);
            //创建byte数组
            byte[] buy = "我有一只小毛驴,我从来也不骑。".getBytes();
            outputStream.write(buy);  //将数组中的信息写入到文件中
            outputStream.close();  //将流关闭
        } catch (Exception e) {
            e.printStackTrace();
        }

        //读出字节流
        try {
            //创建FileInputStream对象
            FileInputStream fileInputStream = new FileInputStream(file);
            byte[] byt = new byte[1024];  //创建数组
            int len = fileInputStream.read(byt);
            //将文件中的信息输出
            System.out.println("文件中的信息是:" + new String(byt, 0, len));
            fileInputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

带缓存的输入/输出流

flush()方法用于即使在缓存区没有满的情况下也将缓存区的内容强制写入外设,习惯上称这个过程为刷新,flush()方法只对使用缓存区的OutputStream类的子类有效,调用close()方法时,系统关闭流之前,也会将缓存区中的信息刷新到磁盘文件

  • BufferedInputStream和BufferedOutputStream类
    • 可以对所有的InputStream类进行带缓存区的包装以达到性能的优化
    • 构造方法
    • BufferedInputStream(InputStream in, int size); size默认32字节
    • BufferedOutputStream(InputStream in, int size); size默认32字节
  • BufferedReader和BufferedWriter类
    • 分别继承Reader类和Writer类
    • 常用方法
    • BufferedReader
      • read(); 读取单个字符
      • readLine(); 读取一个文本行,返回字符串。无数据可读返回null
    • BufferedWriter
      • write(String s, int off, int len); 写入字符串的一部分
      • flush(); 刷新流的缓存
      • newLine(); 写入一个行分隔符
package IO;

import java.io.*;

public class students {
    public static void main(String[] agrs) {
        String[] contents = {"好久不见", "最近好吗", "常联系"};

        //写数据
        File file = new File("students.txt");  //创建File对象
        try {
            FileWriter fw = new FileWriter(file);  //创建FileWriter对象
            BufferedWriter bufw = new BufferedWriter(fw);
            for (int i = 0; i < contents.length; i++) {
                bufw.write(contents[i]);
                bufw.newLine();
            }
            bufw.close();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        //读数据
        try {
            FileReader fr = new FileReader(file);  //创建FileReader对象
            BufferedReader bufw = new BufferedReader(fr);
            String s = null;  //创建字符串对象
            int i = 0;
            //如果文件的文本行数不为null则进入循环
            while ((s = bufw.readLine()) != null) {
                i++;
                System.out.println("第" + i + "行:" + s);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

数据输入/输出流

  • DataInputStream类和DataOutputStream类,允许以与机器无关的方式从底层输入流中读取基本的java数据类型
  • 构造方法
    • DataInputStream(InputStream in); 从指定的基础InputStream创建一个DataInputStream
    • DataOutputStream(OutStream out); 创建一个新的数据输出流,将数据写入指定基础输出流
      • writeBytes(String s); 由于java字符是unicode编码,双字节,只写入字节的低字节内容
      • writeChars(String s); 两个字节都写入
      • writeUTF(String s); 按照utf编码后的字节长度写入
package IO;

import java.io.*;

public class Example {
    public static void main(String[] args) {
        File file = new File("hi.txt");
        try {
            FileOutputStream fs = new FileOutputStream(file);
            DataOutputStream ds = new DataOutputStream(fs);
            ds.writeUTF("使用writeUTF()方法写入数据");  //使用这个写,可以读出来
            ds.writeBytes("使用writeBytes()方法写入数据");
            ds.writeChars("使用writeChars()方法写入数据");
            ds.close();  //关闭流

            FileInputStream fis = new FileInputStream(file);
            DataInputStream dis = new DataInputStream(fis);
            System.out.println(dis.readUTF());
            dis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

ZIP压缩输入/输出流

java的内置类提供了非常好用的相关类,java.util.zip包中的ZipOutputStream和ZipInputStream类实现文件的压缩/解压缩

  • ZipOutputStream类,可将文件压缩为.zip文件
    • 构造方法ZipOutputStream(OutputStream out);
  • ZipInputStream类可读取ZIP压缩格式的文件,包括已压缩和未压缩的条目entry
    • 构造方法ZipInputStream(InputStream in);

//压缩文件
package IO;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class MyZip {
    private void zip(String zipFileName, File inputFile) throws Exception {
        //创建ZipOutputStream对象
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
        zip(out, inputFile, "");  //调用方法
        System.out.println("Ziping...");  //输出信息
        out.close();  //关闭流
    }

    private void zip(ZipOutputStream out, File f, String base) throws Exception {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
//            if (base.length() != 0) {
//                out.putNextEntry(new ZipEntry(base + "\\")); //写入此目录的entry??
//            }
            for (int i = 0; i < fl.length; i++) {
                zip(out, fl[i], base + fl[i]);
            }
        } else {
            out.putNextEntry(new ZipEntry(base));  //创建新的进入点
            FileInputStream in = new FileInputStream(f);
            int b;
            System.out.println(base);
            while ((b = in.read()) != -1) {  //如果没有达到流的尾部
                out.write(b);                 //将自己写入当前ZIP条目
            }
            in.close();
        }
    }

    public static void main(String[] args) {
        MyZip book = new MyZip();
        try {
            book.zip("D:\\zi.zip", new File("src\\number"));
            System.out.println("done.");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
//解压缩
package IO;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class MyZip2 {
    public static void main(String[] args) {
        File file = new File("D:\\zi.zip");  //当前压缩文件
        try {
            ZipFile zipFile = new ZipFile(file);  //创建压缩文件对象
            ZipInputStream zin = new ZipInputStream(new FileInputStream(file)); //创建ZipInputStream对象
            ZipEntry entry = zin.getNextEntry();  //跳过根目录,获取下一个ZipEntry
            while (((entry = zin.getNextEntry()) != null)
                    && !entry.isDirectory()) {  //entry不为空,并不在同一目录下
                File tmp = new File("D:\\" + entry.getName());  //解压出的文件路径
                if (!tmp.exists()) {
                    tmp.getParentFile().mkdirs();  //创建文件父类文件夹路径
                    OutputStream os = new FileOutputStream(tmp);  //将文件目录中的文件放入输出流
                    InputStream in = zipFile.getInputStream(entry);  //用输入流读取压缩文件中制定目录中的文件
                    int count = 0;
                    while ((count = in.read()) != -1) {  //如果有输入流可以读取到数值
                        os.write(count);                 //输入流写入
                    }
                    os.close();  //关闭输出流
                    in.close();  //关闭输入流
                }
                zin.closeEntry();  //关闭当前entry
                System.out.println(entry.getName() + " unzip successfully.");
            }
            zin.close();  //关闭流

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

16 反射

Class类与反射

java.lang.reflect

所有类都继承与Object类,在Object类中定义了一个getClass方法,该方法返回一个类型为Class的对象,利用Class类的对象可以访问的主要信息。getFields()和getMethods()方法依次获得权限为public的成员变量和方法,包含从父类继承的成员变量和方法。getDeclaredFields()和getDeclaredMethods()只获得在本类中定义的所有成员变量和方法。

访问构造方法

通过上面表中构造方法访问,返回Constructor类型的对象或数组。每个Constructor对象代表一个构造方法。利用每个Constructor对象可以操纵相应的构造方法。

package ex16_reflect;

public class Example_01 {
    String s;
    int i;
    int i2;
    int i3;

    private Example_01() {
    }

    protected Example_01(String s, int i) {
        this.s = s;
        this.i = i;
    }

    public Example_01(String... strings) throws NumberFormatException {
        if (0 < strings.length) {
            i = Integer.valueOf(strings[0]);
        }
        if (1 < strings.length) {
            i2 = Integer.valueOf(strings[1]);
        }
        if (2 < strings.length) {
            i3 = Integer.valueOf(strings[2]);
        }
    }

    public void print() {
        System.out.println("s = " + s);
        System.out.println("i = " + i);
        System.out.println("i2 = " + i2);
        System.out.println("i3 = " + i3);
    }
}
//============================================================================

package ex16_reflect;

import java.lang.reflect.Constructor;

public class Main_01 {
    public static void main(String[] args) {
        Example_01 example = new Example_01("10", "20", "30");
        Class<? extends Example_01> exampleC = example.getClass();
        Constructor[] declaredConstructors = exampleC.getDeclaredConstructors();  //获得所有构造方法
        for (int i = 0; i < declaredConstructors.length; i++) {  //遍历所有构造方法
            Constructor<?> constructor = declaredConstructors[i];
            System.out.println("查看是否允许带有可变数量的参数:" + constructor.isVarArgs());  //是否有可变参数
            
            System.out.println("该构造方法的入口参数类型依次为:");
            Class[] parameterTypes = constructor.getParameterTypes();  //获取所有参数类型
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.println("" + parameterTypes[j]);
            }
            
            System.out.println("该构造方法可能抛出的异常类型为:");
            Class[] exceptionTypes = constructor.getExceptionTypes();  //获取所有异常类型
            for (int j = 0; j < exceptionTypes.length; j++) {
                System.out.println(" " + exceptionTypes[j]);
            }
            
            Example_01 example2 = null;
            while (example2 == null) {
                try {
                    if (i == 2) {
                        example2 = (Example_01) constructor.newInstance();
                    } else if (i == 1) {
                        example2 = (Example_01) constructor.newInstance("7", 5);
                    } else {
                        Object[] parameters = new Object[]{new String[]{"100", "200", "300"}};
                        example2 = (Example_01) constructor.newInstance(parameters);
                    }
                } catch (Exception e) {
                    //该构造方法权限为private,默认不允许使用反射利用newInstance()创建对象,先执行setAccessible(true);则允许创建
                    System.out.println("在创建对象时抛出异常,下面执行setAccessible()方法");  
                    constructor.setAccessible(true);
                }
            }
            if (example2 != null) {
                example2.print();
                System.out.println();
            }
        }
    }
}

访问成员变量

返回Field类型的对象或数组,每个Field对象代表一个成员变量,利用Filed对象可以操纵相应的成员变量。

package ex16_reflect;

public class Example_02 {
    int i;
    public float f;
    protected boolean b;
    private String s;
}

//=========================================================

package ex16_reflect;

import java.lang.reflect.Field;

public class Main_02 {
    public static void main(String[] args) {
        Example_02 example = new Example_02();
        Class exampleC = example.getClass();  //使用getClass()获得Class对象
        Field[] declaredFields = exampleC.getDeclaredFields();  //获得所有成员变量
        for (int i = 0; i < declaredFields.length; i++) {
            Field field = declaredFields[i];  //遍历成员变量
            System.out.println("名称为:" + field.getName());  //获取成员变量名称
            Class fieldType = field.getType();
            System.out.println("类型为:" + fieldType);  //获取成员变量类型

            boolean isTrue = true;
            while (isTrue) {
                //如果该成员变量的访问权限为private,则抛出异常,则不允许访问
                try {
                    isTrue = false;
                    System.out.println("修改前的值:" + field.get(example));

                    if (fieldType.equals(int.class)) {  //判断变量类型是否是int类型
                        System.out.println("利用方法setInt()修改成员变量的值");
                        field.setInt(example, 168);
                    } else if (fieldType.equals(float.class)) {  //判断变量类型是否是float类型
                        System.out.println("利用方法setFloat()修改成员变量的值");
                        field.setFloat(example, 99.9F);
                    } else if (fieldType.equals(boolean.class)) {
                        System.out.println("利用方法setBoolean()修改成员变量的值");
                        field.setBoolean(example, true);
                    } else {
                        System.out.println("利用方法set()修改成员变量的值");
                        field.set(example, "MWQ");  //可以为各种类型的成员变量赋值
                    }
                    System.out.println("修改后的值:" + field.get(example));
                } catch (Exception e) {
                    //String s;权限为private,需要先设置setAccessible()再修改
                    System.out.println("在设置成员变量时抛出异常,下面执行setAccessible()方法");
                    field.setAccessible(true);
                    isTrue = true;
                }
            }
            System.out.println("---------------------------");
        }


        Field field = declaredFields[3];
        try {
            field.set(example, "hi");
            System.out.println(field.get(example));
            field.setAccessible(false);  //修改过一次后就一直是true了
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

访问方法

通过getMethod()等方法访问方法时,返回Method()类型的对象和数组。每个Method对象代表一个方法。利用Method对象可以操纵相应的方法。

package ex16_reflect;

public class Example_03 {
    static void staticMethod() {
        System.out.println("执行staticMethod()方法");
    }

    public int publicMethod(int i) {
        System.out.println("执行publicMethod()方法");
        return i * 100;
    }

    protected int protectedMethod(String s, int i) throws NumberFormatException {
        System.out.println("执行protectedMethod()方法");
        return Integer.valueOf(s) + i;
    }

    private String privateMethod(String... strings) {
        System.out.println("执行privateMethod()方法");
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < strings.length; i++) {
            stringBuffer.append(strings[i]);
        }
        return stringBuffer.toString();

    }
}
//============================================================

package ex16_reflect;

import java.lang.reflect.Method;

public class Main_03 {
    public static void main(String[] args) {
        Example_03 example = new Example_03();
        Class exampleC = example.getClass();
        Method[] declaredMethods = exampleC.getDeclaredMethods();  //获取所有方法
        for (int i = 0; i < declaredMethods.length; i++) {
            Method method = declaredMethods[i];  //遍历方法
            System.out.println("名称为:" + method.getName());  //获取方法名称

            System.out.println("是否允许带有可变数量的参数:" + method.isVarArgs());  //是否有可变参数

            System.out.println("入口参数类型依次为:");  //入口参数类型
            Class[] parameterTypes = method.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.println(" " + parameterTypes[j]);
            }

            System.out.println("返回值类型:" + method.getReturnType());  //返回值类型

            System.out.println("可能抛出的异常:");
            Class[] exceptionTypes = method.getExceptionTypes();  //可能抛出的异常类型
            for (int j = 0; j < exceptionTypes.length; j++) {
                System.out.println(" " + exceptionTypes[j]);
            }

            boolean isTrue = true;
            while (isTrue) {
                //如果方法访问权限为private抛出异常,不允许访问
                try {
                    isTrue = false;
                    if ("staticMethod".equals(method.getName())) {
                        method.invoke(example);  //执行没有入口参数的方法staticMethod
                    } else if ("publicMethod".equals(method.getName())) {
                        System.out.println("返回值为:"
                                + method.invoke(example, 168));  //执行方法
                    } else if ("protectedMethod".equals(method.getName())) {
                        System.out.println("返回值为:"
                                + method.invoke(example, "7", 5));  //执行方法
                    } else if ("privateMethod".equals(method.getName())) {
                        Object[] parameters = new Object[]{new String[]{"M", "W", "Q"}};  //定义二维数组
                        System.out.println("返回值为:"
                                + method.invoke(example, parameters)); //执行方法
                    }

                } catch (Exception e) {
                    //privateMethod()权限为private需要先setAccessible(true);才可以访问
                    System.out.println("在执行方法时出现异常,执行下面的setAccessible()方法");
                    method.setAccessible(true);
                    isTrue = true;
                }
            }
            System.out.println();
        }
    }
}

使用Annotation功能

定义Annotation类型

  • 使用@interface,这个关键字的隐含意思是继承java.lang.annotation.Annotation接口
  • 使用@Annotation,设置Annotation类型使用的成员元素种类,使用枚举类ElementType中的枚举常量设置
  • 使用@Retention,设置Annotation的有效范围,使用枚举类RetentionPolicy中的枚举常量设置

package ex16_reflect;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.CONSTRUCTOR)  //用于构造方法
@Retention(RetentionPolicy.RUNTIME)  //在运行时加载Annotation到JVM中
public @interface Constructor_Annotation {
    String value() default "默认构造方法";  //定义一个具有默认值的String型成员
}
package ex16_reflect;

import java.lang.annotation.*;
//用于字段、方法、参数
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})

//在运行时加载Annotation到JVM中
@Retention(RetentionPolicy.RUNTIME)

public @interface Field_Method_Parameter_Annotation {
    String describle();               //定义一个没有默认值的String型成员
    Class type() default void.class;  //定义一个具有默认值的Class型成员
}

访问Annotation信息

在定义Annotation类型时,@Retention设置为RetentionPolicy.RUNTIME,那么在程序运行时通过反射就可以获取到相关的Annotation信息,如获取构造方法、字段、方法的Annotation信息。

  • Constructor、Field、Method均继承了AccessableObject类,在AccessibleObject中定义了3个关于Annotation的方法
    • isAnnotationPresent(Class<? extends Annotation> annotationClass); 查看是否添加了指定类型的Annotation
    • getAnnotation(Class<T> annotationClass); 获得指定类型的Annotation,存在返回相应对象,不存在返回null
    • getAnnotations(); 返回所有Annotation,返回一个数组
  • 在Constructor和Method中还定义了方法getParameterAnnotations(); 用来获得所有参数添加的Annotation,将以Annotation类型的二维数组返回,在数组中的顺序与声明顺序相同,没有参数则返回长度为0的数组;未添加Annotation参数用长度为0的嵌套数组占位

在Record类中运用前面定义的Annotation类型@Constructor_Annotation和@Field_Method_Parameter_Annotation.

package ex16_reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Record {
    @Field_Method_Parameter_Annotation(describle = "编号", type = int.class)
    int id;
    @Field_Method_Parameter_Annotation(describle = "姓名", type = String.class)
    String name;

    @Constructor_Annotation()
    public Record() {
    }

    @Constructor_Annotation("立即初始化构造方法")
    public Record(
            @Field_Method_Parameter_Annotation(describle = "编号", type = int.class) int id,
            @Field_Method_Parameter_Annotation(describle = "姓名", type = String.class) String name) {
        this.id = id;
        this.name = name;
    }

    @Field_Method_Parameter_Annotation(describle = "获得编号", type = int.class)
    public int getId() {
        return id;
    }

    @Field_Method_Parameter_Annotation(describle = "设置编号")
    public void setId(
            @Field_Method_Parameter_Annotation(describle = "编号", type = int.class) int id) {
        this.id = id;
    }

    @Field_Method_Parameter_Annotation(describle = "获得姓名", type = String.class)
    public String getName() {
        return name;
    }

    @Field_Method_Parameter_Annotation(describle = "设置姓名")
    public void setName(
            @Field_Method_Parameter_Annotation(describle = "姓名", type = String.class) String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Record record = new Record();
        Class recordC = record.getClass();

        //========================访问构造方法(Constructor)====================================
        System.out.println("=====访问构造方法(Constructor)=======");
        Constructor[] declaredConstructors = recordC.getDeclaredConstructors();  //获得所有构造方法
        for (int i = 0; i < declaredConstructors.length; i++) {  //遍历构造方法
            Constructor constructor = declaredConstructors[i];
            //判断是否是指定类型的注释
            if (constructor.isAnnotationPresent(Constructor_Annotation.class)) {
                //获取指定类型的注释
                Constructor_Annotation ca = (Constructor_Annotation) constructor
                        .getAnnotation(Constructor_Annotation.class);
                System.out.println(ca.value());   //获得注释信息
            }
            //获得参数的注释
            Annotation[][] parameterAnnotations = constructor.getParameterAnnotations();
            for (int j = 0; j < parameterAnnotations.length; j++) {
                //获得指定参数注释的长度
                int length = parameterAnnotations[j].length;
                if (length == 0) {  //长度为0表示没有为该参数添加注释
                    System.out.println("未添加Annotation的参数");
                } else {
                    for (int k = 0; k < length; k++) {
                        //获得参数的注释
                        Field_Method_Parameter_Annotation pa =
                                (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
                        System.out.println("  " + pa.describle());  //获得参数描述
                        System.out.println("  " + pa.type());       //获得参数类型
                    }
                }
            }
            System.out.println();
        }

        //============================访问字段(Field)=====================================
        System.out.println("=======访问字段(Field)==========");
        Field[] declaredFileds = recordC.getDeclaredFields();  //获得所有字段
        for (int i = 0; i < declaredFileds.length; i++) {
            Field field = declaredFileds[i];
            //查看是否具有指定类型的注释
            if (field.isAnnotationPresent(Field_Method_Parameter_Annotation.class)) {
                //获得指定类型的注释
                Field_Method_Parameter_Annotation fa = field
                        .getAnnotation(Field_Method_Parameter_Annotation.class);
                System.out.println("  " + fa.describle());  //获得字段的描述
                System.out.println("  " + fa.type());       //获得字段的类型
            }
            System.out.println();
        }

        //============================访问方法(Method)及包含参数的Annotation信息================
        System.out.println("===访问方法(Method)及包含参数的Annotation信息===");
        Method[] methods = recordC.getDeclaredMethods();  //获得所有方法
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            //查看是否有指定类型的注释
            if (method.isAnnotationPresent(Field_Method_Parameter_Annotation.class)) {
                //获得指定类型的注释
                Field_Method_Parameter_Annotation ma = method
                        .getAnnotation(Field_Method_Parameter_Annotation.class);
                System.out.println("  " + ma.describle());  //获得方法的描述
                System.out.println("  " + ma.type());       //获得方法的返回值类型
            }
            //获得参数的注释
            Annotation[][] parameterAnnotations = method
                    .getParameterAnnotations();
            for (int j = 0; j < parameterAnnotations.length; j++) {
                int length = parameterAnnotations[j].length;
                if (length == 0) {
                    System.out.println("未添加Annotation的参数");
                } else {
                    for (int k = 0; k < length; k++) {
                        //获得指定类型的注释
                        Field_Method_Parameter_Annotation pa =
                                (Field_Method_Parameter_Annotation) parameterAnnotations[j][k];
                        System.out.println("  " + pa.describle());  //获得参数描述
                        System.out.println("  " + pa.type());       //获得参数类型
                    }
                }
            }
            System.out.println();
        }
    }
}

17 枚举类型与泛型

枚举类型

  • 使用接口设置常量
  • 枚举类型,相当于一个类,继承于java.lang.Enum,每一个枚举类型成员是一个实例,默认被public、static、final修饰,使用时直接使用枚举类型名称调用
    • 枚举类型常用方法
    • 枚举类型中的构造方法
    • 可以在每个枚举成员内部实现接口中的方法(每个成员都要实现)

//枚举类型的定义与使用
package ex17_enum;

interface Constants {
    public static final int Constants_A = 1;  //将常量放在接口中
    public static final int Constants_B = 12;
}

public class ConstantsTest {
    enum Constants2 {
        Constants_1, Constants_2 //将常量放在枚举类型中
    }

    //使用接口定义的常量
    public static void doit(int c) {
        switch (c) {
            case Constants.Constants_A:
                System.out.println("doit() Constants_A");
                break;
            case Constants.Constants_B:
                System.out.println("doit() Constants_B");
                break;
            default:
                System.out.println("others");
        }
    }

    //使用枚举类型定义的常量
    public static void doit2(Constants2 c) {
        switch (c) {
            case Constants_1:
                System.out.println("doit2() Constants_1");
                break;
            case Constants_2:
                System.out.println("doit2() Constants_2");
                break;
        }
    }

    public static void main(String[] args) {
        ConstantsTest.doit(Constants.Constants_A);  //使用接口中定义的常量
        ConstantsTest.doit(3);

        ConstantsTest.doit2(Constants2.Constants_1);  //使用枚举类型中的常量
        ConstantsTest.doit2(Constants2.Constants_2);
//        ConstantsTest.doit2(3);  //会报错,因为doit2()的入口参数为Constants2枚举类型

    }
}
//枚举类型中的常用方法
package ex17_enum;

public class ShowEnum {
    enum Constants2 {
        CONSTANTS_1, CONSTANTS_2, CONSTANTS_3  //将常量放在枚举类型中
    }

    public static void main(String[] args) {
//        Constants2[] x = Constants2.values();

        //.values() 打印出枚举成员变量
        //.ordinal() 获取枚举类型成员的索引位置
        for (int i = 0; i < Constants2.values().length; i++) {
            System.out.println("枚举类型成员变量:"
                    + Constants2.values()[i] +
                    "位置索引:" + Constants2.values()[i].ordinal());
        }

        //.compareTo() 比较定义顺序
        //.valueOf() 将普通字符串转化为枚举类型,必须已经定义
        for (int i = 0; i < Constants2.values().length; i++) {
            int result = Constants2.valueOf("CONSTANTS_2").compareTo(Constants2.values()[i]);
            System.out.println("Constants2.CONSTANTS_2与" + Constants2.values()[i] + "比较" + result);
        }
    }
}
//枚举类型中的构造方法
package ex17_enum;

public class EnumIndexTest {
    enum Constants2 {  //将常量放在枚举类型中
        CONSTANTS_1("我是枚举成员1"),  //定义带参数的枚举常量
        CONSTANTS_2("我是枚举成员2"),
        CONSTANTS_3("我是枚举成员3"),
        CONSTANTS_4(4);
        private String description;
        private int i = 1;

        private Constants2() {
        }  //无参数构造方法  private防止用户实例化一个枚举对象

        private Constants2(String description) {  //参数为String的构造方法
            this.description = description;
        }

        private Constants2(int i) {  //参数为int的构造方法
            this.i = i;
        }

        public String getDescription() {  //获取description的值
            return description;
        }

        public int getI() {  //获取i的值
            return i;
        }

    }

    public static void main(String[] args) {
        Constants2[] cs = Constants2.values();  //获取枚举变量数组
        for (int i = 0; i < cs.length; i++) {
            Constants2 c = cs[i];  //枚举变量
            System.out.println(c + "调用getDescription()方法:" + c.description
                    + " 调用getI()方法:" + c.getI());
        }
    }
}
//在枚举对象中实现接口中的方法
package ex17_enum;

interface d {
    String getDescription();  //默认public

    int getI();
}

public enum AnyEnum implements d {
    CONSTANTS_1 {
        public String getDescription() {
            return "我是枚举类型1";
        }

        public int getI() {
            return i;  //i必须是static,否则使用this.i调用对象的i
        }
    },
    CONSTANTS_2 {
        public String getDescription() {
            return "我是枚举类型2";
        }

        public int getI() {
            return i;  //i必须是static,否则使用this.i调用对象的i
        }
    };

    private static int i;

    public static void main(String[] args) {
        AnyEnum[] anyEnums = AnyEnum.values();  //返回枚举类型数组
        for (int i = 0; i < anyEnums.length; i++) {  //每一个枚举对象调用方法
            AnyEnum anyEnum = anyEnums[i];
            System.out.println(anyEnum + "调用getDescription()方法:" + anyEnum.getDescription()
                    + " 调用getI()方法:" + anyEnum.getI());
        }
    }
}

泛型

向上转型是安全的,但是向下转型时用错了类型或者并没有执行该操作,可以被编译器接受,但是执行时会出现ClassCastException异常。这样看来,在向下转型时通常会出现问题,使用泛型机制有效的解决这一问题,在编译时就会进行类型检查

  • 定义泛型类
  • 泛型的常规用法
    • 定义泛型类时声明多个类型 MultiOverClass<T1, T2>; MultiOverClass<Boolean, Float> x = new MultiOverClass<>();
    • 定义泛型类时声明数组类型 private T[] array;
    • 集合类声明容器的元素
      • Map: HashMap, TreeMap
      • Set: HashSet, TreeSet
      • List: ArrayList, TreeList
  • 泛型的高级用法
    • 限制泛型可用类型 没有使用限制时,默认Object类下的所有子类都可以实例化泛型对象
      • class 类名称<T extends anyClass> anyClass是任何接口或类
      • 泛型类的类型必须实现或继承了anyClass这个接口或类,使用关键字extends
    • 使用通配符
      • 主要作用是在创建一个泛型类对象时限制这个泛型类对象的类型实现继承某个接口或类的子类
      • 泛型类名称<? extends List> a = null; 泛型类对象a只能被赋值对象为实现List的子类或接口
      • List<?> list 定义的泛型类对象只能获取或删除元素,不能添加新信息(因为是?编译器不知道添加什么信息)
    • 泛型的向上限制
      • 使用super关键字
      • A<? super List> a = null; 表示对象a只接受List接口或上层父类类型
    • 定义为泛型的类和接口也可以被继承和实现
      • 如果没有在继承时指明泛型的继承,在子类中都会默认为Object类型

总结:

  • 泛型的类型参数只能是类类型,不可以是简单类型
  • 泛型的类型参数可以是多个
  • 可以使用extends super关键字限制泛型的类型
  • 可以使用通配符限制泛型的类型

//泛型的定义
package ex17_enum;

public class OverClass<T> {  //定义泛型类
    private T over;    //定义泛型类成员变量

    public T getOver() {  //设置getXXX()方法
        return over;
    }

    public void setOver(T over) {  //设置setXXX()方法
        this.over = over;
    }


    public static void main(String[] args) {
        //实例化一个Boolean类型的对象
        OverClass<Boolean> over1 = new OverClass<Boolean>();
        over1.setOver(true);          //不需要向上转型
        Boolean b = over1.getOver();  //不需要向下转型
        System.out.println(b);

        //实例化一个Float类型的对象
        OverClass<Float> over2 = new OverClass<Float>();  //Float可以不写
        over2.setOver(12.3F);       //不需要向上转型
        Float f = over2.getOver();  //不需要向下转型
        System.out.println(f);
    }
}
//定义泛型时,声明多个类型,声明数组
package ex17_enum;

public class MultiOverClass<T1, T2> {
    private T1 multiOver;
    private T2[] multiOver2;  //声明数组类型
//    private T2[] multiOver2 = newT[10];  //不能使用泛型建立数组实例,因为不能确定类型,无法分配内存

    public void setMultiOver(T1 multiOver, T2[] multiOver2) {
        this.multiOver = multiOver;
        this.multiOver2 = multiOver2;
    }

    public T1 getMultiOver() {
        return multiOver;
    }

    public T2[] getMultiOver2() {
        return multiOver2;
    }


    public static void main(String[] args) {
        //自动检测类型,不需要向上转型
        MultiOverClass<Boolean, String> multiOverClass = new MultiOverClass<>();
        multiOverClass.setMultiOver(true, new String[]{"成员1", "成员2"});

        //使用getXXX()方法,不需要向下转型
        Boolean b = multiOverClass.getMultiOver();
        String[] str = multiOverClass.getMultiOver2();

        //输出实例multiOverClass
        System.out.println("getMultiOver: " + b);
        for (String s : str) {
            System.out.println("getMultiOver2: " + s);
        }

    }

}
//集合类声明元素类型
package ex17_enum;

import java.util.*;

public class AnyClass {
    public static void main(String[] args) {
        //Map集合(又称为容器):
        //HashMap 添加和删除映射关系效率高 不保证顺序 允许为null
        //TreeMap 其中的映射关系存在一定顺序 效率比HashMap稍差 键不允许为null
        Map<Integer, String> map = new HashMap<>();
        Map<Integer, String> map1 = new TreeMap<>();
        //为容器填充键和键值
        map.put(33333, "A");
        map1.put(33333, "A");
        map.put(11111, "A");
        map1.put(11111, "A");
        map.put(22222, "B");
        map1.put(22222, "B");
        //获取键和键值--使用迭代器 map不保证顺序 map1排序
        Set<Integer> keySet = map.keySet();  //利用Set构建Map集合中的key集合
        Collection<String> valueColl = map.values();  //利用Collection构建Map中的values集合 值有可能相同
//        Set<Integer> keySet = map1.keySet();  //利用Set构建Map集合中的key集合
//        Collection<String> valueColl = map1.values();  //利用Collection构建Map中的values集合 值有可能相同
        Iterator<Integer> keyIterator = keySet.iterator();
        Iterator<String> valueIterator = valueColl.iterator();
        while (keyIterator.hasNext()) {
            System.out.println("key: " + keyIterator.next()
                    + " value: " + valueIterator.next());
        }


        //Set
        //HashSet 不保证顺序
        //TreeSet 有一定顺序 也可以按照指定比较器递增排序
        Set<String> set = new HashSet<>();
        Set<String> set1 = new TreeSet<>();
        set.add("33333");
        set1.add("33333");
        set.add("11111");
        set1.add("11111");
        set.add("22222");
        set1.add("22222");
        Iterator<String> setIterator = set.iterator();
        Iterator<String> setIterator1 = set1.iterator();
        System.out.println("--------HashSet不保证顺序---------");
        while (setIterator.hasNext()) {
            System.out.println(setIterator.next());
        }
        System.out.println("--------TreeSet增序---------");
        while (setIterator1.hasNext()) {
            System.out.println(setIterator1.next());
        }


        //List
        //ArrayList 可以根据索引位置快速访问 缺点是插入删除速度慢
        //LinkedList 随机访问比ArrayList效率低 优点是插入删除效率比ArrayList高
        List<String> stringList = new ArrayList<>();
        List<String> stringList1 = new LinkedList<>();

    }
}
//泛型的高级用法,限制类型,通配符
package ex17_enum;


import java.util.ArrayList;
import java.util.List;

public class debug {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("成员1");
        stringList.add("成员2");
        List<String> stringList1 = stringList;  //没有使用通配符 引用赋值 可添加新信息
        System.out.println("-------删除之前的stringList-------------");
        for (String str : stringList) {
            System.out.println(str);
        }
        stringList1.remove(0);
        System.out.println("-------删除之后的stringList-------------");
        for (String str : stringList) {
            System.out.println(str);
        }
        stringList1.add("成员3(stringList1添加的)");
        System.out.println("-------添加成员-------------");
        for (String str : stringList) {
            System.out.println(str);
        }

        System.out.println("----------------------------");
        List<?> stringList2 = stringList;  //使用List<?> 引用赋值 不能添加新信息 只能获取或删除
        System.out.println("获取信息:" + stringList2.get(1));
        System.out.println("删除信息:" + stringList2.remove(1));
//        stringList2.add("不可添加成员");
//        stringList2.set(0,"不可添加成员");  //不能添加新信息
        for (String str : stringList) {
            System.out.println(str);
        }
    }
}

18 多线程

实现线程的两种方式

每一个程序都是一个进程,一个进程可以有多个线程,线程是进程的一个执行流程

  • 继承java.lang.Thread类
  • 实现java.lang.Runnable接口 (java不支持多继承,在需要继承其他类又需要实现多线程利用接口实现)
  • 线程的生命周期:出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态、死亡状态

操作线程的方法

  • 线程的休眠 Thread.sleep(),sleep为Thread的静态方法,单位毫秒
  • 线程的加入 thread.join(),在线程A中加入线程B,执行完B后在执行A
  • 线程的中断 thread.interrupt(),引发InterruptedException,通常在catch中关闭数据库连接和socket连接等操作。提倡run中使用无线循环的方式,然后使用一个布尔型标记控制循环的停止。
  • 线程的礼让 yield(),对于多任务操作系统不需要使用,操作系统会自动分配CPU时间片

线程的优先级

系统始终选择就绪状态下优先级较高的线程进入运行状态。优先级高的并不一定先执行,也取决于操作系统

线程同步

多线程资源同步的问题都是采用给定时间只允许一个线程访问共享资源,给资源上一把锁

  • 同步块synchronized(),将对资源的操作放在同步块中
  • 同步方法,在方法前加synchronized修饰,某个对象调用同步方法时必须等该同步方法执行完毕后才被执行
//继承Thread
package ex18_Thread;

public class ThreadTest extends Thread {
    private int count = 10;
    public void run() {  // 覆盖run()方法
        while (true) {
            System.out.print(count + " ");
            if (--count == 0)
                return;
        }
    }

    public static void main(String args[]) {
        ThreadTest t1 = new ThreadTest();
        ThreadTest t2 = new ThreadTest();
        t1.start();  // 线程执行调用start()
        t2.start();
    }

}

 

//使用Runnable接口实现多线程
package ex18_Thread;

import javax.swing.*;
import java.awt.*;
import java.net.URL;
import java.util.EmptyStackException;

public class SwingAndThread extends JFrame {
    private JLabel jl = new JLabel();  // 声明JLable对象
    private static Thread t;  //声明线程对象
    private int count = 0;  //声明计数变量
    private Container container = getContentPane();  //声明容器

    public SwingAndThread() {
        setBounds(100,100,1000,1000);  //绝对定位窗体大小和位置
        container.setLayout(null);  //不使用任何布局管理器
        URL url = SwingAndThread.class.getResource("/ex18_Thread/1.gif");  //获取图标url
        Icon icon = new ImageIcon(url);  //实例化一个icon
        jl.setIcon(icon);  //将图标放在标签中
        jl.setHorizontalAlignment(SwingConstants.LEFT); //将图片放在左边
//        jl.setBounds(10,10,500,500);  //设置标签位置和大小
        jl.setOpaque(true);  //设置不透明

        t = new Thread(new Runnable() {  //定义匿名内部类,该类实现Runnable接口
            @Override
            public void run() {  //重写run()
                while (count <= 500) {
                    jl.setBounds(count,10,500,500);
                    try {
                        Thread.sleep(1000);  //线程休眠1000毫秒
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    count += 4;
                    if (count == 500) {  //当其回到最右边时回到最左边
                        count = 10;
                    }
                }

            }
        });
        t.start();  //启用一个新线程start(), start()运行run()方法
        container.add(jl);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

    }
    public static void main(String args[]) {
        new SwingAndThread();
    }


}

 

//线程的休眠
package ex18_Thread;

import javax.swing.*;
import java.awt.*;
import java.util.Random;

public class SleepMethodTest extends JFrame {
    private Thread t;
    private static Color[] color = {Color.BLACK, Color.BLUE,Color.CYAN,Color.RED};
    private static final Random rand = new Random();  //创建随机对象
    private static Color getC() {
        return color[rand.nextInt(color.length)];
    }

    public SleepMethodTest() {
        t = new Thread(new Runnable() {
            int x = 100;
            int y = 100;
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //获取组件绘图上下文对象
                    Graphics graphics = getGraphics();
                    graphics.setColor(getC());  //设置绘图颜色
                    graphics.drawLine(x, y+=10, 800, y);
                    if (y >= 800) {
                        y = 100;
                    }
                }
            }
        });
        t.start();
    }

    public static void main(String args[]) {
        init(new SleepMethodTest(),1000,1000);
    }

    //设置窗体的各种属性的方法
    public static void init(JFrame frame, int width, int height) {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(width, height);
        frame.setVisible(true);
    }

}

 

//线程的加入
package ex18_Thread;

import javax.swing.*;
import java.awt.*;

public class JoinTest extends JFrame {
    private Thread threadA;
    private Thread threadB;
    final JProgressBar jProgressBar = new JProgressBar();
    final JProgressBar jProgressBar1 = new JProgressBar();
    int count = 0;
    public JoinTest() {
        super();
        getContentPane().add(jProgressBar, BorderLayout.NORTH);
        getContentPane().add(jProgressBar1, BorderLayout.SOUTH);
        jProgressBar.setStringPainted(true);
        jProgressBar1.setStringPainted(true);
        //使用匿名内部类初始化Thread实例
        threadA = new Thread(new Runnable() {
            int count = 0;
            @Override
            public void run() {
                while (true) {
                    jProgressBar.setValue(count++);
                    try {
                        Thread.sleep(100);
                        threadB.join();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        threadA.start();

        threadB = new Thread(new Runnable() {
            int count = 0;
            @Override
            public void run() {
                while (true) {
                    jProgressBar1.setValue(count++);
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (count + 1 == 100)
                        break;
                }
            }
        });
        threadB.start();

    }



    public static void main(String args[]) {
        init(new JoinTest(), 100,100);
    }

    //设置窗体的各种属性的方法
    public static void init(JFrame frame, int width, int height) {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(width, height);
        frame.setVisible(true);
    }
}

 

//线程的中断
package ex18_Thread;

import javax.swing.*;
import java.awt.*;

public class InterruptedSwing extends JFrame {
    Thread thread;

    public InterruptedSwing() {
        super();
        final JProgressBar progressBar = new JProgressBar();
        getContentPane().add(progressBar, BorderLayout.NORTH);
        progressBar.setStringPainted(true);
        thread = new Thread(new Runnable() {
            int count = 0;
            @Override
            public void run() {
                while (true) {
                    progressBar.setValue(count++);
                    try {
                        thread.sleep(1000);
                    } catch (InterruptedException e) {
                        System.out.println("当前程序被中断");
                        break;
                    }
                }


            }
        });
        thread.start();
        thread.interrupt();
    }

    public static void main(String[] args) {
        init(new InterruptedSwing(), 100, 100);
    }

    //设置窗体的各种属性的方法
    public static void init(JFrame frame, int width, int height) {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(width, height);
        frame.setVisible(true);
    }
}

19 网络通信

网络程序设计基础

  • TCP/IP协议中有两种协议:TCP、UDP
  • 端口和套接字
    • 端口:0-65535,通过不同的端口连接不同的服务,HTTP80,FTP21,
    • 套接字: 用来连接应用程序与端口,类似于插座连接电线与电器

TCP程序设计基础

java.net.InetAdress类,与IP地址相关的类,InetAdress类的方法会抛出UnknownHostException,所以必须进行异常处理

java.net.ServerSocket类用于表示服务器套接字

UDP程序设计基础

java.net.DatagramPacket类表示数据包,java.net.DatagramSocket类表示发送和接受数据包的套接字

 

package ex19_Net;

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAdress_test {
    public static void main(String args[]) {
        InetAddress ip;
        try {
            ip = InetAddress.getLocalHost();
            String localname = ip.getHostName();
            String localip = ip.getHostAddress();
            System.out.println("name: " + localname);
            System.out.println("ip: " + localip);


        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }


}

 

package ex19_Net;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TCP_test {
    private BufferedReader reader;
    private ServerSocket server;
    private Socket socket;
    void getserver() {
        try {
            server = new ServerSocket(8998);
            System.out.println("服务器套接字创建成功");
            while (true) {
                System.out.println("等待客户机的连接");
                socket = server.accept();
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                getClientMessage();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void getClientMessage() {
        try {
            while (true) {
                System.out.println("客户机:" + reader.readLine());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            if (reader != null) {
                reader.close();
            }
            if (socket != null) {
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {
        TCP_test tcp = new TCP_test();
        tcp.getserver();
    }

}

 

package ex19_Net;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintWriter;
import java.net.Socket;

public class TCP_test_client extends JFrame {
    private PrintWriter writer;
    Socket socket;
    private JTextArea ta = new JTextArea();
    private JTextField tf = new JTextField();
    Container cc;
    public TCP_test_client(String title) {
        super(title);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cc = this.getContentPane();
        final JScrollPane scrollPane = new JScrollPane();
        scrollPane.setBorder(new BevelBorder(BevelBorder.RAISED));
        getContentPane().add(scrollPane, BorderLayout.CENTER);
        scrollPane.setViewportView(ta);
        cc.add(tf,"South");
        tf.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                writer.println(tf.getText());
                ta.append(tf.getText() + '\n');
                ta.setSelectionEnd(ta.getText().length());
                tf.setText("");
            }
        });
    }

    private void connect() {
        ta.append("尝试连接\n");
        try {
            socket = new Socket("127.0.0.1",8998);
            writer = new PrintWriter(socket.getOutputStream(),true);
            ta.append("完成连接\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String args[]) {
        TCP_test_client client = new TCP_test_client("向服务器发生数据");
        client.setSize(200,200);
        client.setVisible(true);
        client.connect();
    }
}

20 数据库操作

JDBC常用的类和接口

  • Connection接口
  • Statement接口
  • PreparedStatement接口
  • DriveManage类
  • ResultSet接口

数据库操作

  • 连接数据库
  • 向数据库发送SQL语句
  • 处理查询结果集
  • 顺序查询
  • 模糊查询
  • 预处理语句
  • 添加修改删除记录

 

 

 

posted on 2019-11-28 14:22  Bingmous  阅读(389)  评论(0编辑  收藏  举报