Java基础

文章目录

  • 第一章
    • 1.1 IDEA常用快捷键
    • 1.2Java环境变量的配置
  • 第二章
    • 2.1数组
      • 2.1.1动态初始化数组的格式
      • 2.1.2 静态初始化数组格式
      • 2.1.3 求数组长度
      • 2.1.4 定义和使用二维数组
      • 2.1.5 获取二维数组的行数与特定行的个数
      • 2.1.6 方法的返回值为数组
    • 2.2 输入和输出
      • 2.2.1 Scanner类进行输入
      • 2.2.2 输出
    • 2.3 随机数的产生
  • 第三章
    • 3.1 类和对象
      • 3.1.1对象的创建及其使用
    • 3.2 ArrayList类的使用
      • 3.2.1常用方法
      • 3.2.2 使用 (添加、删除、集合长度)
    • 3.3字符串的创建和使用
      • 3.3.1 String和char的转换
      • 3.3.2 equals(比较两个字符串是否相同)方法的使用
      • 3.3.3字符长度(length)、字符拼接(concat)、指定字符索引位置(charAt)、查找字符第一次出现位置(indexOf)
      • 3.3.4 截取字符串中的一段字符(substring)
      • 3.3.5 将字符串转化为字符数组(toCharArray)、将字符串转化为字节数组(getBytes)、将字符串中的某段字符串替换为另一串字符串(replace)
      • 3.3.6 对一串字符串进行切割(split)
      • 3.3.7 把数组按照指定格式拼接成一个字符串
    • 3.4 Arrays工具类的使用
      • 3.4.1 toString(将数组转化为默认字符串[])和sore(将数组按照升序排列)的使用
      • 3.4.2 数组填充(Array.fill)、二分查找查找元素(Arrays.binarySearch)
    • 3.5 Math类的使用
  • 第四章
    • 4.1 继承
      • 4.1.1访问本类成员变量和父类成员变量
      • 4.1.2 方法的覆盖重写
      • 4.1.3 继承父类的方法并添加新的内容
      • 4.1.4 继承关系中,父子类构造方法的访问特点
      • 4.1.5 this关键字的使用
      • 4.1.6 抽象方法和抽象类的使用
    • 4.2 接口
      • 4.2.1 使用接口
      • 4.1.2 默认方法的定义和使用
      • 4.1.3 静态方法的使用
      • 4.1.4 私有方法
      • 4.1.5 接口中的成员变量
      • 4.1.6 总结
      • 4.1.7 继承父类并实现多个接口
    • 4.3 多态
      • 4.3.1 介绍
      • 4.3.2 向上转型
      • 4.3.3 向下转型
      • 4.3.4 instanceof关键字的使用
      • 4.3.5 final关键字
    • 4.4 内部类
      • 4.4.1 局部内部类
      • 4.4.2 匿名内部类
  • 第五章
    • 5.1 Object类
      • 5.1.1equals方法
    • 5.2 Data类
      • 5.2.1 Data类的构造方法和成员方法
      • 5.2.2 使用DateFormat类中的方法format,把日期格式化为文本
      • 5.2.3使用DateFormat类中的方法parse,把文本解析为日期
    • 5.3 System类
      • 5.3.1 计算程序运行时间
    • 5.4 Calendar类
      • 5.4.1 Calendar介绍
      • 5.4.2Calendar类常用的成员方法
    • 5.5 StringBuilder类
      • 5.5.1 字符串变量.append(数据):往变量中添加数据
      • 5.5.2 String和StringBuffer的转换(toString方法的使用)
  • 第六章
    • 6.1 异常处理
      • 6.1.1 异常
      • 6.1.2 运行时异常
      • 6.1.3异常处理(try-catch-finally)
      • 6.1.4 Throwable异常处理的三个方法
      • 6.1.5 异常处理(throws)
      • 6.1.6自定义异常类
    • 6.2 包装类
      • 6.2.1基本类型与字符串类型的转换
    • 6.3 注解
  • 第七章
    • 7.1 Collection集合
      • 7.1.1 Collection中常用的方法
    • 7.2 Iterator接口
      • 7.2.1常用的方法
      • 7.2.2迭代器的使用步骤
    • 7.3 增强for循环
    • 7.4 List接口
    • 7.5 LinkedList集合
      • 7.5.1 LinkedList接口中带索引的方法
    • 7.6 Set接口
      • 7.6.1 Set接口特点
      • 7.6.2 HashSet集合
        • 7.6.2.1hashSet集合存储数据结构
          • 7.6.2.2HashSet存储自定义类型的元素
      • 7.6.3 hasCode()方法
      • 7.6.4 LinkedHashSet集合
    • 7.7 可变参数
    • 7.8 集合工具类Collectiongs常用方法
    • 7.9 Map集合
      • 7.9.1 Map集合中常用方法
      • 7.9.2 Map集合遍历
    • 7.10 HashMap存储自定义类型键值
    • 7.11 添加方法的优化of方法
  • 第八章
    • 8.1 泛型
    • 8.2 多线程
      • 8.2.1 多线程介绍
      • 8.2.2 多线程的创建1
      • 8.2.3 Thread常用方法
      • 8.2.4 多线程的创建2(实现Runnable接口)
        • 实现Runnable接口创建多线程程序的好处
      • 8.2.5 线程安全问题
        • 1.同步代码块(synchronized)
        • 2.同步方法
        • 3.使用Lock锁
    • 8.3线程状态
      • 8.3.1Waiting(无限等待状态)
      • 8.3.2Time Waiting(计时状态)
      • 8.3.3线程通信
      • 8.3.4 线程池
    • 8.4 Lambda表达式
      • 8.4.1Lambda表达式与传统方法的对比
      • 8.4.2使用Lambda表达式对对象进行排序
      • 8.4.3 Lambda表达式的使用前提
      • 8.4.4 Lambda表达式是可省略的
    • 8.5 File类
      • 8.5.1 File的四个静态成员变量
      • 8.5.2 File类的构造方法
      • 8.5.3 File类常用方法
      • 8.5.4 判断功能的方法
      • 8.5.5 创建删除功能的方法
      • 8.5.6 File类遍历(文件夹)目录功能
      • 8.5.6 FileFilter过滤器的原理和使用
    • 8.6 IO流
      • 8.6.1字节流
        • 1.常用方法
        • 2.续写和换行
      • 8.6.2 字节输入流
      • 8.6.3 文件读写练习
      • 8.6.4字符流
      • 8.6.5 字符输出流
      • 8.6.6 使用Properties集合
        • 1.存储数据、遍历
        • 2.输出流(持久化写入到硬盘中)store
        • 3.输入流
      • 8.6.7 缓冲流
        • 1.字节缓存输出流
        • 2.字节缓存输入流
        • 3.字符缓冲输入流
        • 4.字符缓冲输出流
      • 8.6.8 转换流
      • 8.6.9 序列化和反序列化
        • 1. 序列化
        • 2.反序列化
        • 3.瞬态关键字transient
      • 8.6.10打印流
  • 第九章
    • 9.1网络编程入门
      • 9.1.1 常用端口号
      • 9.1.2 Socket类
      • 9.1.3 服务器与客户端间的通信
      • 9.1.4 文件上传
    • 9.2 函数式接口
      • 9.2.1 函数式接口的使用
      • 9.2.2 常用的函数式接口
        • 1.Supplier< T >接口
        • 2.Consumer< T >接口
        • 3.Predicate< T > 接口
        • 4.Function接口
    • 9.3 Stream流
      • 9.3.1 Stream流常用方法
      • 9.3.3 方法引用
    • 9.4 junit单元测试
    • 9.5 反射
      • 9.5.1 获取成员变量们
      • 9.5.2 获取构造方法
      • 9.5.3 获取成员方法们
    • 9.6 Optional类
      • 常用方法
    • 9.7 JDK9&10&11新特性
      • 1. 模块系统
      • 2.jshell(交互式的编程环境)
      • 3. Java 9 私有接口方法
      • 9.5.3 获取成员方法们
    • 9.6 Optional类
      • 常用方法
    • 9.7 JDK9&10&11新特性
      • 1. 模块系统
      • 2.jshell(交互式的编程环境)
      • 3. Java 9 私有接口方法

第一章

1.1 IDEA常用快捷键

快捷键 功能
Alt+Enter 导入包,自动修正代码
Ctrl+Y 删除光标所在行
Ctrl+D 复制光标所在行内容,插入光标位置下面
Ctrl+Alt+L 格式化代码
Ctrl+/ 单行注释
Ctrl+Shift+/ 选中代码注释多行注释
Alt+Insert 自动生成代码
Shift+F6 整体选中一个变量
Alt+Shift+上下箭头 移动当前代码行
Ctrl+X 删除行
fori/sout/psvm + Tab 生成循环/输出/主函数代码
Ctrl+Alt+T 生成try catch
Ctrl + O 重写方法
Ctrl + I 实现方法
Ctr+Shift+U 大小写转化
ALT+/ 代码提示
Ctrl+Shift+J 整合两行为一行
Ctrl+空格 代码提示
Ctrl+Alt+L 格式化代码
Ctrl+Alt+I 自动缩进
Ctrl+Alt+O 优化导入的类和包
Ctrl+E 最近更改的代码
Shift+enter 另起一行
Ctrl+Z 倒退(撤销)
Ctrl+Shif+Z 向前(取消撤销)
鼠标左键单击行号 Debug追踪
F8 逐行执行程序
F7 进入到方法中
Shift+F8 跳出方法
Ctrl+F2 推出debug模式,停止调试
Console 切换到控制台

1.2Java环境变量的配置

  • 学习Java最好选用Java SE提供的Java软件开发工具箱JDK,可以登录官方网址,免费下载Java SE提供的JDK。

  • 系统环境的设置

    • 设置系统变量JAVA_HOME:变量名:JAVA_HOME,变量值:JDK的位置
    • 系统环境Path的设置:变量名:Path,变量值:%JAVA_HOME%\bin
  • 查看JDK安装位置:命令行窗口执行java -verbose

第二章

2.1数组

2.1.1动态初始化数组的格式

  • 数据类型 [ ]数组名称=new 数据类型 [数组长度]
int n=new Scanner(System.in).nextint;
int [] arrey=new int[n];

2.1.2 静态初始化数组格式

  • 数据类型 [ ]数组名称=new 数据类型{元素1,元素2…}

2.1.3 求数组长度

int []arrer=new int{1,2,3,4,5,6,7,8,9,10,1,1};
int n;
n=arerry.length;

2.1.4 定义和使用二维数组

  • 定义:数据类型 [][]数组名=new 数据类型[行个数] [列个数]
int [] [] num=null;
num=new int[3] [];
num[0]=new int[4];
num[1]=new int[5];
num[2]=new int[6];

2.1.5 获取二维数组的行数与特定行的个数

  • 数组名.length //取得数组的行数

  • 数组名[行的索引].length

  • 定义数组若不赋值应让其指向空指针

2.1.6 方法的返回值为数组

public static void main(String[] args) {
    float[] result = calculate(10, 20, 30);
    System.out.println(result[0]);
    System.out.println(result[1]);
}

public static float[] calculate(float i, float i1, float i2) {
    float sum = i + i1 + i2;
    float avg = sum / 3;
    float[] arrey = {sum, avg};
    return arrey;
}

2.2 输入和输出

2.2.1 Scanner类进行输入

import java.util.Scanner;
Scanner sc=new Scanner(System.in);//定义sc为Scanner的对象,System.in代表从键盘输入
int n=sc.nextInt();//Int表示数据类型,若数据类型为float则为sc.nextFloat
String str=sc.next();//sc.next()代表从键盘获取字符串

2.2.2 输出

JDK1.5新增了和C语言中printf函数类似的输出数据的方法,格式如下

  • %d:输出int类型的数据
  • %c:输出char类型的数据
  • %f:输出浮点型类型的数据,小数部分最多保留六位
  • %s:输出字符串数据,输出数据时也可以控制数据在命令行的位置,例如
  • %md:输出的int型数据占m列
  • %m.nf:输出的浮点型数据占m列,小数点保留n位

2.3 随机数的产生

import java.util.Random;
Randon rand=new Random();
int[]a=null;
a=new int[rand.nextInt(10)];//返回一个[0,10)的随机整数  //a.length可求数组长度
a=new int[rand.nextInt(10)+1];//返回一个[1,11)的随机整数  //a.length可求数组长度

第三章

3.1 类和对象

3.1.1对象的创建及其使用

  • 创建一个对象后会执行一次无参的构造方法
public class Main {
    public static void main(String[] args) {
        Student stu = new Student();//创建一个无参对象
        Student stu1 = new Student(100, "小明");//创建一个有参对象
        System.out.println(stu1.num);
        System.out.println(stu1.name);
        stu.setName("小华");//为对象stu.name赋值
        stu.setNum(101);//为对象stu.num赋值
        System.out.println(stu.getNum());//提取对象里的成员内容
        System.out.println(stu.getName());
        stu.setNum(new Scanner(System.in).nextInt());//从键盘获取一个数字并赋值给成员变量

    }

    public static class Student {
        private int num;
        private String name;

        public int getNum() {
            return num;
        }

        public void setNum(int num) {
            this.num = num;
        }//this.代表指向本类的成员

        public String getName() {
            return name;
        }

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

        public Student() {
        }//无参构造方法

        public Student(int num, String name) {
            this.num = num;
            this.name = name;
        }//有参构造方法
    }
}

3.2 ArrayList类的使用

3.2.1常用方法

  • boolean add(E e):将指定元素添加到该集合尾部
  • boolean remove(Object o):移除此列表中首次出现的指定元素(如果存在)
  • int size():返回此列表中的元素数。

3.2.2 使用 (添加、删除、集合长度)

import java.util.ArrayList
public class Main{    
	public static void main(String[] args) {
        ArrayList<String>list=new ArrayList<>();//创建集合list为String型
        //基本类型为int或char型时它们的包装类为Integer或Character其他的数据类型首字母均大写
        System.out.println(list);//[]
        list.add("小明");
        System.out.println(list);//[小明]
        list.add("小华");
        list.add("小红");
        String name=list.get(1);//获取元素
        System.out.println(name);//小华
       list.remove(2);//删除第三个元素
        System.out.println(list);//[小明, 小华]
        int size=list.size();
        System.out.println(list.size());//2
    }
}

3.3字符串的创建和使用

3.3.1 String和char的转换

public class Main {
    public static void main(String[] args) {
        String str1=new String();
     System.out.println(str1);//输出为空,小括号留空说明什么都没有
        char []chararray={'a','b','b'};
        String str2=new String(chararray);
        System.out.println(str2);//输出abc,将char数组转化为字符串
        byte []bytearray={97,98,99};
        String str3=new String(bytearray);
        System.out.println(str3);//输出为abc
        System.out.println(str3==str2);//false(两个字符串保存的地址不同)
    }

}

3.3.2 equals(比较两个字符串是否相同)方法的使用

public class Main {
    public static void main(String[] args) {
        String str1="hello";
        String str2="Hello";
        char []charArrey={'h','e','l','l','o'};
        String str3=new String(charArrey);
        System.out.println(str1.equals(str2));//false(区分大小写)
        System.out.println(str1.equalsIgnoreCase(str2));//true(不区分大小写)
        System.out.println(str1.equals(str3));//true
        System.out.println(str3.equals("hello"));//true
        System.out.println("hello".equals(str1));//true
        String str5=null;
        System.out.println("abc".equals(str5));//false
        //System.out.println(str5.equals("abc"));//空指针异常
    }
}

3.3.3字符长度(length)、字符拼接(concat)、指定字符索引位置(charAt)、查找字符第一次出现位置(indexOf)

public class Main {
    public static void main(String[] args) {
        String str1="hello";
        String str2="world";
        //字符串长度
        int length="好好学习,天天向上。".length();
        System.out.println(str1.length());//5
        System.out.println(length);//10
        //字符串拼接
        String str3=str1.concat(str2);
        System.out.println(str3);//helloworld
        //获取指定索引位置的单个字符
        char ch="hello".charAt(4);
        System.out.println(ch);//o
        char ch1=str1.charAt(4);
        System.out.println(ch1);//o
        //查找参数字符串在本来字符串中第一次出现的位置
        String str4="fddvsvvwew";
        int index=str4.indexOf("vsvv");
          System.out.println("helloworld".indexOf("abc"));//-1
        System.out.println(index);//3(找得到返回位置,找不到返回-1)
    }
}

3.3.4 截取字符串中的一段字符(substring)

public class Main {
    public static void main(String[] args) {
        //public String substring(int index);//截取从参数位置一直到末尾的字符串
        //public String substring(int index,int end);//截取从begin开始到end的字符串,包含begin,不包含end
        String str1="hello world";
        String str2=str1.substring(6);
        String str3=str1.substring(6,8);
        System.out.println(str2);
        System.out.println(str3);
    }
}

3.3.5 将字符串转化为字符数组(toCharArray)、将字符串转化为字节数组(getBytes)、将字符串中的某段字符串替换为另一串字符串(replace)

public class Main {
    public static void main(String[] args) {
        //将字符串转化为字符数组
        String str1="hello";
        char []chars=str1.toCharArray();
        System.out.println(chars[2]);//l
        //将字符串转化为字节数组
        byte []bytes ="abc".getBytes();
        for (int i = 0; i < bytes.length; i++) {
            System.out.println(bytes[i]);
        }//97 98 99
        //将字符串中的某段字符串替换为另一串字符串
        String str2="How do you do";
        String str3=str2.replace("o","*");
        System.out.println(str3);//H*w d* y*u d*
    }
}

3.3.6 对一串字符串进行切割(split)

public class Main {
    public static void main(String[] args) {
        String str1="aaa,bbb,ccc,d";
        String []array1=str1.split(",");
        for (int i = 0; i < array1.length; i++) {
            System.out.println(array1[i]);
        }//aaa bbb ccc d
        String str2="xxx.yyy.zzz";
        String []array2=str2.split("\\.");
        for (int i = 0; i < array2.length; i++) {
            System.out.println(array2[i]);
        }
        //ps:若分隔符为"."则添加到split中时需加两个反斜杠
    }
}

3.3.7 把数组按照指定格式拼接成一个字符串

//定义一个方法,把数组{1,2,3}按照指定格式拼接成一个字符串。格式[word1#word2#word3]
public class Main {
    public static void main(String[] args) {
        int[] array = {1, 2, 3};
        String result = fromArrayToString(array);
        System.out.println(result);
    }

    public static String fromArrayToString(int[] array) {
        String str="[";
        for (int i = 0; i < array.length; i++) {
            if(i==array.length-1){
                str+="word"+array[i]+"]";
            }else {
                str+="word"+array[i]+"#";
            }
        }return str;
    }
}

3.4 Arrays工具类的使用

3.4.1 toString(将数组转化为默认字符串[])和sore(将数组按照升序排列)的使用

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        //将int[]数组按照默认格式变成字符串
        int[] intArrays = {1, 5, 9, 5, 3, 6, 4};
        String intStr = Arrays.toString(intArrays);
        System.out.println(intStr);//[1, 5, 9, 5, 3, 6, 4]
        //将数组按照升序排序
        Arrays.sort(intArrays);
        System.out.println(Arrays.toString(intArrays)); //[1, 3, 4, 5, 5, 6, 9]
        //按照字典序排序
        String[] StringArray = {"abc", "bcd", "ace"};
        Arrays.sort(StringArray);
        String stringStr = Arrays.toString(StringArray);
        System.out.println(stringStr);  //[abc, ace, bcd]
    }
}

3.4.2 数组填充(Array.fill)、二分查找查找元素(Arrays.binarySearch)

import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        int[]arr=new int[5];
        Arrays.fill(arr,2,4,5);//将数组索引值[2-4)填充为5
        System.out.println(Arrays.toString(arr));//[0, 0, 5, 5, 0]
        Arrays.fill(arr,9);//将数组内的元素填充为5
        System.out.println(Arrays.toString(arr));//[9, 9, 9, 9, 9]
        int[] arr1=new int[]{556,5688,568,568,568,3854,923};
        int a=Arrays.binarySearch(arr1,568);//用二分查找在arr1中查找568
        System.out.println(a);//3 返回索引位置,若没找到返回一个负数
    }
}

3.5 Math类的使用

  • 常用方法的使用
public class Main {
    public static void main(String[] args) {
        System.out.println(Math.max(5,9));//返回两个数中最大的数字
        System.out.println(Math.abs(-0.25));//0.25绝对值
        System.out.println(Math.addExact(5,7));//12  求和
        System.out.println(Math.cbrt(8.0));   //2  求立方根
        System.out.println(Math.ceil(5.2));//6 向上取整
        System.out.println(Math.floor(5.2));//5向下取整
        System.out.println(Math.pow(5,3));   //125.0 pow(x,y) x的y次方
        System.out.println(Math.round(5.6));    //6 四舍五入
        System.out.println(Math.PI);   // 3.141592653589793
        System.out.println(Math.E);    // 2.718281828459045
    }
}

第四章

4.1 继承

4.1.1访问本类成员变量和父类成员变量

public class Main {//主函数
    public static void main(String[] args) {
        Zi zi=new Zi();
        zi.method();
        System.out.println(zi.age);//子类的成员涵盖父类的成员
    }
}

public class Fu {//父类
    int num=30;
    int age=20}

public class Zi extends Fu {//子类 !用extends表示继承+对象
    int num=10;
    public void method(){
        int num=20;
        System.out.println(num);  //遇到同名的情况若变量为局部变量则直接写
        System.out.println(this.num);//若为本类的成员变量则用this.成员变量
        System.out.println(super.num);//若为父类的成员变量,则用super.成员变量
    }
}

4.1.2 方法的覆盖重写

  • 必须保证父子类之间方法的名称相同,参数列表也相同。

  • @Override:写在方法前面可以检测是不是有效的覆盖重写

  • 子类方法的返回值必须小于等于父类方法的返回值范围(Object类是所有类的公共最高父类)

  • 子类方法的访问权限必须大于等于父类方法的权限修饰符

  • (public>protected>(default)>private)

4.1.3 继承父类的方法并添加新的内容

public class Main {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.show();//显示号码  显示姓名
    }
}

public class Fu {
    public void show(){
        System.out.println("显示号码");
    }
}

public class Zi extends Fu {
    @Override
    public void show() {//覆盖重写
        super.show();//继承父类的方法
        System.out.println("显示姓名");
    }
}

4.1.4 继承关系中,父子类构造方法的访问特点

  • 子类构造方法中有一个默认隐含的”super()“调用,所以一定是先调用了父类构造后执行子类构造。

  • 子类构造可以通过super关键字来调用父类重载构造。

  • super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。

4.1.5 this关键字的使用

  • 在本类的成员方法中,访问本类的成员变量

  • 在本类的成员方法中,访问本类的另一个方法

  • 在本类的构造方法中,访问本类的另一个方法

  • this(…)调用也必须是构造方法的第一个语句,唯一一个

4.1.6 继承的三个特点

  • Java语言是单继承的

  • Java语言可以多级继承

  • 一个父类可以有多个子类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twYEcn04-1587174732280)(C:\Users\asus\Desktop\Java.assets\C431C138C9FFA623AC7F8DA6E7599C73.jpg)]

4.1.6 抽象方法和抽象类的使用

抽象方法:就是加上abstract关键字,然后去掉大括号直接分号结束

抽象类:抽象方法所在的类,必须是抽象类才行,在class之前加上abstract即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-si2yuIn9-1587174732283)(C:\Users\asus\Desktop\Java.assets\67DE2A85C32938100B88B754D7D57AAD.jpg)]

如何使用抽象类和抽象方法

1.不能直接创建new抽象类对象

2.必须用一个子类来继承抽象父类

3.子类必须覆盖重写抽象父类当中所有的抽象方法,否则编译器会报错,除非子类也是抽象类(子类去掉abstract关键字,然后补上方法体大括号)

4.创建子类对象并使用

注意: 1.抽象类中可以有构造方法,是供子类创建对象时(默认赠送一个super()方法),初始化父类成员 使用的;

​ 2.抽象类中不一定含有抽象方法,但是有抽象方法的一定是抽象类

public class Main {
    public static void main(String[] args) {
        Cat cat = new Cat();//输出:一只猫(创建对象会执行构造方法)
        cat.eat();//输出:吃鱼
    }
}
public abstract class Animal {
    public abstract void eat();
}
public class Cat extends Animal {
    public Cat() {//构造方法
        System.out.println("一只猫");
    }
    @Override
    public void eat() {
        System.out.println("吃鱼");
    }
}

4.2 接口

1.接口就是多个类的公共规范

2.接口是一种引用数据类型,最重要的内容就是其中的抽象方法。

3.接口可以包含的内容有(常量、抽象方法、默认方法、静态方法、私有方法)

4.接口中的方法一定是抽象方法因此可以选择性省略public abstract

5.如果实现类没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。

4.2.1 使用接口

1.接口不能直接使用,必须有一个实现类来实现接口

2.接口的实现类必须覆盖重写(实现)接口中的所有抽象方法

3.创建实现类的对象进行使用

public class Main {
    public static void main(String[] args) {
        MyInterfaceAbstractImpl impl=new MyInterfaceAbstractImpl();//不可以创建接口的对象,只能用实现类来实现接口
        impl.methodAbs();
        impl.methodAbs1();
    }
}
public interface MyInterfaceAbstract {//定义一个接口
    public abstract  void methodAbs();//以下四个均为抽象方法(接口里面的方法体均为抽象方法)
    public   void methodAbs1();
   abstract  void methodAbs2();
   void methodAbs3();
}
public class MyInterfaceAbstractImpl implements MyInterfaceAbstract {
	//覆盖重写接口里的所有抽象方法,如果没有全部覆盖重写,那么该类必须为抽象类
    @Override
    public void methodAbs() {
        System.out.println("这是第一个方法");
    }

    @Override
    public void methodAbs1() {
        System.out.println("这是第二个方法");

    }

    @Override
    public void methodAbs2() {
        System.out.println("这是第三个方法");

    }

    @Override
    public void methodAbs3() {
        System.out.println("这是第四个方法");

    }
}

4.1.2 默认方法的定义和使用

1.接口的默认方法,可以通过接口实现类对象直接调用。

2.接口的默认方法,也可以被接口实现类进行覆盖重写

public class Main {
    public static void main(String[] args) {
        MyInterfaceAbstractImpl impl=new MyInterfaceAbstractImpl();
        impl.methodAbs();
        impl.methodDefault();//可以直接调用接口中的默认方法
    }
}
public interface MyInterfaceAbstract {
    public abstract  void methodAbs();
    public default void methodDefault(){//用来实现接口升级问题(假设在已有的基础上想在接口上新添加一个方法,若定义一个抽象方法,则继承接口的类均要重新覆盖重写代码,运用默认方法,则被继承接口的类则会直接实现接口里的默认方法,或者可以在继承接口的类中覆盖重写该方法)
        System.out.println("这是新添加的默认方法");
    }
}
class MyInterfaceAbstractImpl implements MyInterfaceAbstract {

    @Override
    public void methodAbs() {
        System.out.println("覆盖重写了接口中的抽象方法");
    }
}

4.1.3 静态方法的使用

1.不能通过接口实现类的对象来调用接口当中的静态方法

2.通过接口名称直接调用静态方法(静态方法跟实现类的对象没关系)

public class Main {
    public static void main(String[] args) {
        MyInterfaceStatic.Method();//输出:这是接口里的静态方法
    }
}
public interface MyInterfaceStatic {
    public static void Method(){
        System.out.println("这是接口里的静态方法");
    }
}

4.1.4 私有方法

用来解决两个默认方法之间重复的代码,但是这个共有方法不应该让实现类使用,应该是私有化的

1.普通私有方法:解决多个默认方法之间重复代码的问题

2.静态私有方法

public interface MyInterfaceStatic {
    public default void Method1(){
        System.out.println("这是一个默认方法");
        MethodCommon1();
    }
   private void MethodCommon1(){
        System.out.println("DDD");
    }
    public static void Method(){
        System.out.println("这是接口里的静态方法");
        MethodCommon();
    }
    public static void MethodCommon(){
        System.out.println("AAA");
        System.out.println("BBB");
        System.out.println("CCC");
    }
}

4.1.5 接口中的成员变量

1.接口中的成员变量必须使用public static final三个关键字进行修饰(这种成员变量是不可变的,所以可省略)

2.接口当中的常量,必须进行赋值

3.接口中常量的名称,使用完全大写的字母,用下划线分隔

4.使用(接口名称.变量)

4.1.6 总结

接口内容可拥有

1.成员变量其实是常量,格式:

public static final 数据类型 常量名称=数据值;

**注意:**常量必须进行赋值,而且一旦赋值不可改变、常量名称用下划线分隔,并且完全大写

2.接口中最重要的就是抽象方法,格式

public abstract 返回值类型 方法名称(参数列表)

**注意:**实现类必须覆盖重写所有的抽象方法,除非实现类是抽象类

3.从Java8开始,接口允许定义默认方法,格式;

public default 返回值类型 方法名称(参数列表){方法体}

**注意:**默认方法也可以被覆盖重写

4.从Java8开始,接口里允许定义静态方法,格式;

public static 返回值类型 方法名称(参数列表){方法体}

注意: 应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

5.从Java9开始,接口里允许定义私有方法,格式

普通私有方法:private 返回值类型 方法名称(参数列表){方法体}

静态私有方法:private static 返回值类型 方法名称(参数列表){方法体}

**注意: **private的方法只有接口自己能调用,不能被实现类或别人使用。

4.1.7 继承父类并实现多个接口

1.接口没有静态代码块或者构造方法

2.一个类的直接父类是唯一的,但是一个类可以实现多个接口

格式:

public class MyInterfaceImpl MyInterfaceA,MyInterfaceB{覆盖重写所有的抽象方法}

3.如果实现类所实现的多个接口中,存在重复的抽象方法,那么只需覆盖重写一次即可

4.如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类必须是抽象类

5.如果实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对默认方法进行覆盖重写

6.一个类如果直接父类的方法和接口当中的默认方法长生冲突,那么优先用父类的方法

4.3 多态

4.3.1 介绍

1.多态的体现:父类的引用指向自己的子类对象、父类的引用也可以接收自己的子类对象

2.多态的前提:必须是继承(extends)或实现(implements),通常还有一个前提即存在覆盖重写。

3.注意:多态new出来的对象只能调用父类含有的抽象方法(运行时实现的是子类的覆盖重写的方法)不能调用子类自身的方法。若想调用子类自身的方法需向下转型

4.3.2 向上转型

1.格式:父类名称 对象名 = new 子类名称();

2.含义:右侧创建一个子类对象,把它当作父类看待使用

3.类似于:double num=100;//正确,int–>double,自动类型转换

4.3.3 向下转型

1.格式:子类名称 对象名=(子类名称)父类对象

2.含义:将父类对象,还原成为本来的子类对象

3.注意:必须保证对象本来创建的时候就是猫,才能向下转型为猫,否则编译时不会报错,运行时会报错(ClassCastException)

public class Main {
    public static void main(String[] args) {
        Animal animal=new Cat();//把猫当作动物看(向上转型)
        animal.eat();//猫吃鱼
//        animal.CatchMouse(); //错误写法
        Cat cat=(Cat)animal;//向下转型
        cat.CatchMouse();//猫抓老鼠
    }
}
public abstract class Animal {
    public abstract void eat();
}
public class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void CatchMouse(){
        System.out.println("猫抓老鼠");
    }
}

4.3.4 instanceof关键字的使用

public class Main {
    public static void main(String[] args) {
       giveMePet(new Dog());//输出:狗看家
       giveMePet(new Cat());//输出:猫抓老鼠
    }

    private static void giveMePet(Animal animal) {
        if(animal instanceof Dog){
            Dog dog=(Dog)animal;
            dog.WatchHouse();
        }
        if(animal instanceof Cat){
            ((Cat) animal).CatchMouse();
        }
    }
}
public abstract class Animal {
    public abstract void eat();
}
public class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void CatchMouse(){
        System.out.println("猫抓老鼠");
    }
}
public class Dog extends Animal {
    public void eat(){
        System.out.println("狗吃骨头");
    }
    public void WatchHouse(){
        System.out.println("狗看家");
    }
}

4.3.5 final关键字

1.final关键字修饰的类不能有子类

2.final关键字修饰的方法不能被覆盖重写(对于类或方法来说,abstract关键字和final关键字不能同时使 用,因为矛盾)

3.用final修饰的局部变量不能被修改(只能一次赋值)

4.对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选一

5.由于成员变量具有默认值,所以用final之后必须手动赋值

四种权限修饰符
public protected (default) private
同一个类
同一个包 ×
不同包子类 × ×
不同包非子类 × × ×

4.4 内部类

1.内部类用外部类可以随意访问,外部内用内部类,需要内部类对象

2.使用内部类:

​ (1.) 间接方式:在外部类的方法当中,使用内部类,然后main只是调用外部方法。

​ (2.)直接方式:公式

外部类名称.内部类名称 对象名 = new 外部类名称().new 外部类名称()

3.如果出现重名现象,那么格式是:外部类名称.this.外部类成员变量

public class Outer {
    int num=10;//外部类的成员变量
    public class Inter{
        int num=20;//内部类的成员变量
        public void methodInter(){
            int num=30;//内部类方法的局部变量
            System.out.println(num);
            System.out.println(this.num);
            System.out.println(Outer.this.num);
        }
    }
}

4.4.1 局部内部类

1.局部内部类定义在外部类的方法之中

2.局部内部类的修饰符什么都不能写,默认(default)

3.局部内部类只能在所定义方法内访问

4.如果希望访问所在方法的局部变量,那么这个方法必须是有效final(用final修饰或仅一次赋值)

4.4.2 匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况就可以省略掉该类的定义,而改用匿名内部类

public class Main {
    public static void main(String[] args) {
        MyInterface obj=new MyInterface() {//创建一个匿名内部类
            //大括号里面代替了接口的实现类MyInterface
            //obj是对象名,类名称是没有名字的
            @Override//覆盖重写接口里的方法
            public void method() {
                System.out.println("匿名内部类实现啦");
            }
        };//匿名内部类的内容
        obj.method();
    }
}
public interface MyInterface {
    public void method();
}

第五章

5.1 Object类

5.1.1equals方法

  • Object obj:可以传递任意对象
    • equals相当于==比较运算符返回的是一个布尔值false ture
    • 基本数据类型比较的是数值
    • 引用数据类型比较的是地址值
import java.util.Objects;

public class Person {
    String name;
    int age;
    
    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}public class Main {
    public static void main(String[] args) {
        Person p1=new Person("迪丽热巴",18);
        Person p2=new Person("马尔扎哈",19);
        System.out.println(p1);//Demo29.Person@34c45dca
        System.out.println(p2);//Demo29.Person@52cc8049
        System.out.println(p1.equals(p2));//false  地址值不同
        p1=p2;
        System.out.println(p1.equals(p2));//true  地址值相同
    }
}
import java.util.Objects;

public class Person {
    String name;
    int age;

    @Override//覆盖重写equals方法
    public boolean equals(Object o) {
        //比较this和对象o的地址值是否相同
        if (this == o) return true;
        //判断o是否能线下转型为Person类
        if (!(o instanceof Person)) return false;
        //判断两个对象的成员数值是否相同
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
public class Main {
    public static void main(String[] args) {
        Person p1=new Person("迪丽热巴",18);
        Person p2=new Person("马尔扎哈",19);
        Person p3=new Person("马尔扎哈",19);
        System.out.println(p1.equals(p2));//false
        System.out.println(p2.equals(p3));//true  比较两个对象
       									 //的成员数据
    }
}
import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        String s1 = null;
        String s2 = "abc";
// System.out.println(s1.equals(s2));//NullPointerException
        /*
         *Objects类的equals方法;对两个对象进行比较,防止空指针异常
         * public static boolean equals(Object a,Object b){
         * return (a==b)||(a!=null&&a.equals(b));
         * }
         */
        System.out.println(Objects.equals(s1, s2));//false
    }
}

5.2 Data类

5.2.1 Data类的构造方法和成员方法

import java.util.Date;

public class Main {
    public static void main(String[] args) {
       demo01();//Date()无参数保存的为当前时间点
        demo02();//Date(Long data)有参数为将毫秒值转化为日期(记得写上L表示长整型)
        demo03();//date.getTime()返回自0毫秒以来此Date对象表示的毫秒数
    }

    private static void demo03() {
        Date date =new Date();
        System.out.println(date.getTime());//1580267279745 
        date =new Date(2);
        System.out.println(date.getTime());//2
    }

    private static void demo02() {
        Date date =new Date(0L);//Thu Jan 01 08:00:00 CST 1970  中国时间0毫秒
        System.out.println(date);
        date=new Date(685384139945L);
        System.out.println(date);//Sat Sep 21 00:28:59 CST 1991
    }

    private static void demo01() {
        Date date =new Date();
        System.out.println(date);//Wed Jan 29 11:00:20 CST 2020
    }
}

5.2.2 使用DateFormat类中的方法format,把日期格式化为文本

使用步骤

  • 创建SimpleDateFormat对象,构造方法中传递指定的模式

  • 调用SimpleDateFormat对象中的方法format,按照指定的模式,把Date日期格式化为符合模式的字符串(文本)

  • 日期格式化

y M w W D d
年中的周 月中的周 年中的天数 月中的天数
F E H k m S毫秒数
月份中的星期 星期中的天数 一天中的小时数(0-23) 一天中的小时数(1-24) 小时中的分钟数 s分钟中的秒数
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        demo01();

    }

    private static void demo01() {
        SimpleDateFormat simpleDateFormat = new 			SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        Date date = new Date();
        String string = simpleDateFormat.format(date);
        System.out.println(string);//2020年01月29日 11时47分54秒
    }
}

5.2.3使用DateFormat类中的方法parse,把文本解析为日期

  • 使用步骤

1.创建SimpleDateFormat对象,构造方法中传递指定的模式

2.调用SimpleDateFormat对象中的方法parse,把符合构造方法中模式的字符串,解析为Date日期

  • 注意

public Date parse(String source)throws ParseException

parse方法声明了一个异常叫ParseException,如果字符串的构造方法的模式不一样就会抛出此异常,调用一个抛出了异常的方法,就必须去处理这个异常,要么throws继续抛出这个异常,要么try-catch自己处理

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
    public static void main(String[] args) throws ParseException {
        demo02();
    }


    private static void demo02() throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        Date date = simpleDateFormat.parse("2020年01月29日 11时47分54秒");
        System.out.println(date);
    }
}

5.3 System类

5.3.1 计算程序运行时间

public class Main {
    public static void main(String[] args) {
        demo01();
    }

    private static void demo01() {
        long start=System.currentTimeMillis();
        for (int i = 0; i < 9999; i++) {
            System.out.println(i);
        }
        long end=System.currentTimeMillis();
        System.out.println("程序共耗时"+ (end-start)+"毫秒");//程序共耗时46毫秒
    }
}

5.4 Calendar类

5.4.1 Calendar介绍

1.Calendar类是一个抽象类,里面提供了很多操作日历字段的方法

2.Calendar类无法直接创建对象使用,里面有一个静态方法叫getInstance(),该方法放回了Calendar类的子类对象。

import java.util.Calendar;
public class Main {
    public static void main(String[] args)  {
        Calendar calendar = Calendar.getInstance();//多态
        System.out.println(calendar);
    }
}
/*java.util.GregorianCalendar[time=1580297923113,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2020,MONTH=0,WEEK_OF_YEAR=5,WEEK_OF_MONTH=5,DAY_OF_MONTH=29,DAY_OF_YEAR=29,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,HOUR=7,HOUR_OF_DAY=19,MINUTE=38,SECOND=43,MILLISECOND=113,ZONE_OFFSET=28800000,DST_OFFSET=0]
*/

5.4.2Calendar类常用的成员方法

  • public int get(int field):放回给定日历字段的值

  • public void set(int field,int value):将给定的日历字段设置为定值

  • public abstract void add(int field,int mount):根据日历规则,为给定的日历字段添加或减去指定的时间量

  • public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象

import java.util.Calendar;
import java.util.Date;

public class Main {
    public static void main(String[] args) {
        demo01();
        demo02();
        demo03();

    }

    private static void demo01() {
        Calendar calendar = Calendar.getInstance();
        System.out.println(calendar.get(Calendar.YEAR));//2020
        System.out.println(calendar.get(Calendar.MONTH));//0 西方的月份为0-11 东方为1-12
        System.out.println(Calendar.DATE);//5
    }

    private static void demo02() {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, 2098);//设置年
        System.out.println(calendar.get(Calendar.YEAR));//2098
        calendar.add(Calendar.YEAR,-2);
        System.out.println(calendar.get(Calendar.YEAR));//2096
        calendar.set(2035, 5, 8);
        System.out.println(calendar.get(Calendar.YEAR));//2035
    }

    private static void demo03() {
        Calendar calendar=Calendar.getInstance();
        Date date= calendar.getTime();
        System.out.println(date);//Wed Jan 29 20:15:13 CST 2020
    }
}

5.5 StringBuilder类

字符串缓冲区,可以提高字符串的操作效率(可以看作一个长度可以改变的字符串)

5.5.1 字符串变量.append(数据):往变量中添加数据

public class Main {
    public static void main(String[] args) {
       StringBuffer str=new StringBuffer();//StringBuffer()构造一个不带任何字符的字符串生成器,其初始容量为16个字符
       StringBuffer str2=new StringBuffer();
       str2=str.append("abc");//添加后将地址赋给str2
        System.out.println(str);//abc
        System.out.println(str2);//abc
        System.out.println(str2==str);//true
        str2=str.append(1).append("abc").append('@');
        System.out.println(str2);//abc1abc@
    }
}

5.5.2 String和StringBuffer的转换(toString方法的使用)

public class Main {
    public static void main(String[] args) {
        //String-->StringBuffer
        String str="hello";
       StringBuffer str1=new StringBuffer(str);
        StringBuffer(String str)构造一个字符串生成器,并初始化为指定字符串内容
       str1.append("world");
        System.out.println(str1);//helloworld
        //StringBuffer-->String
        String str2=str1.toString();//
        System.out.println(str2);//helloworld
    }
}

第六章

6.1 异常处理

6.1.1 异常

  • java.lang.Throwable:类是Java语言中所有错误或异常的超类
    • Exception:编译期异常,进行编译(写代码)Java程序出现的问题
    • RunimeException:运行期异常,Java程序运行过程出现的问题
    • Error:错误,必须修改源代码,程序才能继续执行
public class Main {
    public static void main(String[] args) {
//        int[]arr=null;
//        getElement(arr,0);//Exception in thread "main" java.lang.NullPointerException: 传递的数组的值为空
        int []arr1={5,2,0};
        getElement(arr1,3);//Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 数组索引越界
    }
    public static int getElement(int[]arr,int index){
        if(arr==null){throw new NullPointerException("传递的数组的值为空");}
        if(index<0||index>arr.length-1){throw new ArrayIndexOutOfBoundsException("数组索引越界");}
        return arr[index];
    }
}

对传递的参数进行合法性判断,判断是否为null,可用Objects里的方法requireNonNull进行判断①和②等价

①if(arr==null){throw new NullPointerException("传递的数组的值为空");}
②Objects.requireNonNull(arr,"传递的数组的值为空");

6.1.2 运行时异常

  • NullPointerException空指针异常

  • ArrayIndexOutOfBoundsException角标越界异常

  • ClassCastException类型转换异常

  • NumberFormatException

  • InputMismatchException输入不匹配

  • ArithmeticException算数异常(除数为0)

6.1.3异常处理(try-catch-finally)

1)try-catch-finally

try{可能出现异常的代码}

catch{异常类型1 变量名1}

catch{异常类型2 变量名2}

//执行完异常处理catch后继续执行后面的代码
//finally可写可不写
//在方法中finally会比return先执行
...

finally{一定会执行的代码}//如果finally中有return语句,这一定放回finally中return的数据

6.1.4 Throwable异常处理的三个方法

  • String getMessage():返回此throwable的简短描述

  • String toString():返回此throwable的详细消息字符串

  • void printStackTrace():JVM打印异常对象,默认方法,异常信息是最全面的

import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            readFile("d:\\a.txt");
        } catch (IOException e) {
            System.out.println(e.getMessage());//文件的后缀名不对
            System.out.println(e.toString());//java.io.IOException: 文件的后缀名不对
            System.out.println(e);//java.io.FileNotFoundException: 文件的路径不是c:\a.txt
            e.printStackTrace();//java.io.FileNotFoundException: 文件的路径不是c:\a.txt
                                //at Demo31.Main.readFile(Main.java:24)
                                //at Demo31.Main.main(Main.java:10)
        }
    }

    public static void readFile(String fileName) throws FileNotFoundException, IOException {
        if (!fileName.endsWith(".txt")) {
            throw new IOException("文件的后缀名不对");
        }
        if (!fileName.equals("c:\\a.txt")) {
            throw new FileNotFoundException("文件的路径不是c:\\a.txt");
        }
    }
}

注意

1.catch的异常类型如果满足子父类关系,则应把子类声明在父类上面

2.常用的异常处理方式 :

①String string =变量名.getMessage(); //将异常信息保存在string中

②print Stack Trace() //在命令行打印异常信息在程序中出错的位置及原因。

public class Main{
    public static void main(String[] args) {
        String str="123";
        str="abc";
        int num;
        try
        {num=Integer.parseInt(str);}
        catch (NumberFormatException e){
            System.out.println("出现了数值转换异常");
            String string =e.getMessage();
            System.out.println(string);
        }
        System.out.println("hello");
    }
}

6.1.5 异常处理(throws)

  • 作用:当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象,可以用throws关键字处理异常对象,会把异常对象的声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理

  • 使用格式:在方法声明时使用

修饰符 返回值类型 方法名(参数列表)throws AAAException,BBBException...{
	throw new AAAException("产生原因");
    throw new BBBException("产生原因");
    ...
}

注意

1.throws关键字必须写在方法声明处

2.throws后面声明的异常必须是Exception或者是Exception的子类

3.方法内部如果抛出了多个异常对象,那么throws后面必须也声明多个异常

如果抛出的多个异常对象有子父类的关系,那么直接声明父类异常即可

4.调用了一个声明抛出异常的方法,我们就必须处理声明的异常,要么继续用throws声明抛出,最终交给 JVM处理,要么try…catch自己处理异常

import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
    //由于FileNotFoundException extends IOException 所以FileNotFoundException可以不写
    public static void main(String[] args) throws FileNotFoundException, IOException {
        readFile("d:\\a.txt");
    }

    public static void readFile(String fileName) throws FileNotFoundException, IOException {
        if (!fileName.endsWith(".txt")) {
            throw new IOException("文件的后缀名不对");
        }
        if (!fileName.equals("c:\\a.txt")) {
            throw new FileNotFoundException("文件的路径不是c:\\a.txt");
        }
    }
}

注意

1.如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不 抛出异常。

2.父类方法没有异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不 能声明抛出

6.1.6自定义异常类

  • 格式:
public class XXException extends Exception (RuntimeException) {
    添加一个空参数的构造方法
        添加一个带异常信息的构造方法
}

注意

1.自定义异常类一般都是以Exception结尾,说明该类是异常类

2.自定义异常类必须是继承Exception或Runtime Exception

继承Exception:那么自定义的异常类就是一个编译器异常,如果方法内部抛出了编译器异常,就必须 处理这个异常

继承RuntimeException:那么自定义异常类就是一个运行期异常,无需处理,交给虚拟机处理

6.2 包装类

装箱、拆箱、自动装箱和自动拆箱的了解

6.2.1基本类型与字符串类型的转换

  • 1.基本类型–>字符串

1.基本类型的值+""

2.包装类的静态方法toString(参数),不是Object类的toString()重载

static String toString (int i)返回一个表示指定整数的String对象

3.String类的静态方法valueOf(参数)

static String valueOf (int i)返回int参数的字符串表示形式

  • 2.字符串–>基本类型

使用包装类的静态方法parseXXX(“字符串”);

Integer类:static int parseInt(String s)

Double类:static int parseDouble(String s)

public class Main {
    public static void main(String[] args) {
        int i = 100;
        String s1 = i + "";
        System.out.println(s1 + 100);//100100
        String s2 = Integer.toString(i);
        System.out.println(s2 + 200);//100200
        String s3 = String.valueOf(i);
        System.out.println(s3 + 200);//100200

        Integer j = Integer.parseInt(s2);
        System.out.println(j + 100);//200
    }
}

6.3 注解

  • 使用

1.生成文档的相关注释

2.JDK内置的三个基本注解

@Override:限定重写父类方法,该注解只能用于方法

@Deprecated:用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择

@SuppressWarnings:抑制编译器警告

第七章

7.1 Collection集合

img

7.1.1 Collection中常用的方法

  • public boolean add(E e):把给定的对象添加到集合中
  • public void clear():清空集合中所有元素
  • public boolean remove(E e):把给定的对象在集合中删除
  • public boolean contains(E e):判断当前集合是否包含给定对象
  • public boolean isEmpty():判断当前集合是否为空
  • public int size():返回集合元素个数
  • public Object[] toArray():把集合中的元素,存储到数组中
import java.util.ArrayList;
import java.util.Collection;

public class Main {
    public static void main(String[] args) {
        //添加和移除集合内的元素
        Collection coll = new ArrayList<>();//多态写法
        coll.add("张三");
        coll.add("李四");
        coll.add("赵五");
        System.out.println(coll);//[张三, 李四, 赵五]  不返回地址说明重写了toString方法
        coll.remove("张三");
        System.out.println(coll);//[李四, 赵五]
        ArrayList b = (ArrayList) coll;
        coll.remove(b.get(0));
        System.out.println(coll);//[赵五]
        System.out.println("======================");
        //判断集合内是否含有该元素
        boolean b1 = coll.contains("赵五");
        System.out.println(b1);//true
        //判断当前集合是否为空
        System.out.println(coll.isEmpty());//false 集合不为空
        //返回集合中元素的个数
        System.out.println(coll.size());//1
        //把集合转换为数组
        Object[] arr = coll.toArray();
        System.out.println(arr[0]);//赵五
        //删除集合中所有元素
        coll.clear();
        System.out.println(coll);//[]
    }
}

7.2 Iterator接口

7.2.1常用的方法

  • boolean hasNext():判断集合中还有没有下一个元素,有就返回true没有返回false

  • E next():放回迭代的下一个元素

注意

Iterator迭代器是一个接口,无法直接使用,需要一个实现类对象

Collection接口中有一个方法叫iterator(),这个方法的返回的就是迭代器对象

7.2.2迭代器的使用步骤

1.使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)

2.使用Iterator接口中的方法hasNext判断还有没有下一个元素

3.使用Iterator接口中的方法next取出集合中的下一个元素

4.Iterator接口也是有泛型的,迭代器的泛型跟着集合走,集合是什么泛型,迭代器就是什么泛型

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

public class Main {
    public static void main(String[] args) {
        Collection coll = new ArrayList<>();
        coll.add("小明");
        coll.add("小华");
        coll.add("小红");
        coll.add("小林");
        coll.add("小德");
        Iterator it =coll.iterator();//获取迭代器的实现类对象,并且会把指针(索引)指向集合的-1索引
        while (it.hasNext()/*判断集合还有没有下一个元素*/) {
            System.out.println(it.next());//1.取出下一个元素2.会把指针向后移一位
        }
    }
}

7.3 增强for循环

增强for循环底层也是迭代器,使用for循环的格式,简化了迭代器书写(是JDK1.5之后出现的新特征

作用 用来遍历集合和数组

格式:

for{//(集合/数组的数据类型 变量名 :集合名/数组名)
    System.out.println(变量名);
}

使用

public class Main {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("小明");
        coll.add("小华");
        coll.add("小红");
        coll.add("小林");
        coll.add("小德");
        for (String s : coll) {
            System.out.println(s);
        }
    }
}

7.4 List接口

1.特点:有序的集合,存储元素和取出元素一致、有索引包含了一些带索引的方法、允许存储相同元素

2.ArrayList接口中带索引的方法(特有)

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

public class Main {
    public static void main(String[] args) {
//        public void add(int index,E element):将指定的元素,添加到该集合中的指定位置上
        List<String> list =new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        System.out.println(list);//[a, b, c, d, e]  重写了toString
        list.add(3,"添加的元素");
        System.out.println(list);//[a, b, c, 添加的元素, d, e]
//        public E remove(int index):移除列表中指定位置的元素,返回的是被删除的元素
        String s = list.remove(2);
        System.out.println("被移除的元素是:"+s);//被移除的元素是:c
//        public E set(int index,E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素
        String setE = list.set(1, "B");
        System.out.println("被替换的元素是:"+setE);//被替换的元素是:b
        System.out.println(list);//[a, B, 添加的元素, d, e]
//        public E get(int index):返回集合中指定位置的元素
        for (String str : list) {
            System.out.print(str + " ");//a B 添加的元素 d e
        }
        System.out.println();
        //使用迭代器遍历
        Iterator<String> it=list.iterator();
        while(it.hasNext()){
            String str1=it.next();
            System.out.print(str1+" ");//a B 添加的元素 d e
        }
    }
}

注意

1.Arrayalist底层是一个数组,元素查询快,增删慢

2.LinkedList集合底层是链表结构,查询慢,增删快

7.5 LinkedList集合

7.5.1 LinkedList接口中带索引的方法

  • public void addFirst(E e):将指定元素插入此列表的开头
  • public void addLast(E e):将指定元素添加到此列表的结尾
  • public void push(E e):将元素堆入此列表所表示的堆栈
  • public E getFirst():返回此列表中的第一个元素
  • public E getLast():返回此列表中的最后一个元素
  • public E removeFirst():移除并返回此列表中的第一个元素
  • public E removeLast():移除并返回此列表中的最后一个元素
  • public E pop():从此列表所表示的堆栈弹出一个元素
  • public boolean isEmpty():如果列表不为空元素,则返回true
import java.util.LinkedList;
public class Main01 {
    public static void main(String[] args) {
        show01();
        show02();
        show03();
    }

    private static void show01() {
        //创建LinkedList集合对象
        LinkedList link =new LinkedList<>();
        //使用add方法添加元素
        link.add("a");
        link.add("b");
        link.add("c");
        System.out.println(link);//[a, b, c]
//      addFirst方法等效于push
        link.addFirst("ww");
        link.push("w");
        System.out.println(link);//[w, ww, a, b, c]
//      addLast方法等效于add
        link.addLast("com");
        System.out.println(link);//[w, ww, a, b, c, com]
    }

    private static void show02() {
        LinkedList link =new LinkedList<>();
        //使用add方法添加元素
        link.add("a");
        link.add("b");
        link.add("c");
        if(!link.isEmpty()) {
            System.out.println(link.getFirst());//a
            System.out.println(link.getLast());//c
        }
    }

    private static void show03() {
        LinkedList link =new LinkedList<>();
        //使用add方法添加元素
        link.add("a");
        link.add("b");
        link.add("c");
        //移除第一个元素和最后一个元素
        //pop()方法和removeFirst()方法等价
        String first=link.removeFirst();
        String last=link.removeLast();
        System.out.println(first+" "+last);//a c
        System.out.println(link);//[b]
    }
}

7.6 Set接口

7.6.1 Set接口特点

1、不允许存储重复元素

2、没有索引,没有索引方法,也不能使用普通for循环遍历

Set集合不存储相同元素的原理

调用hashCode和equals方法

import java.util.HashSet;

public class Main04 {
    public static void main(String[] args) {
        HashSet set = new HashSet<>();
        /**
         * Set集合在调用add方法时,add方法会调用hashCode方法和equals方法,判断元素是否重复
         */
        String s1 = "abc";
        String s2 = "abc";
        set.add(s1);
        //s2和s1的哈希值相同(哈希冲突),且s2.equals(s1)返回true,则不存储s2
        set.add(s2);
        set.add("重地");
        set.add("通话");
        set.add("abc");
        System.out.println(set);//[重地, 通话, abc]
    }
}

7.6.2 HashSet集合

7.6.2.1hashSet集合存储数据结构

jdk1.8版本之后:

哈希表=数组+链表

哈希表=数组+红黑树(链表长度超过八位)(提高查询效率)

import java.util.HashSet;
import java.util.Set;

public class Main02 {
    /*    HashSet特点:
        1.不允许存储重复元素
        2.没有索引,没有带索引的方法,也不能使用普通for循环遍历
        3.是一个无需集合,存储元素和取出元素的顺序有可能不一致
        4.底层是一个哈希表结构(查询速度非常快)*/
    public static void main(String[] args) {
        Set set = new HashSet<>();
        set.add(3);
        set.add(1);
        set.add(2);
        set.add(1);
        for (Integer integer : set) {
            System.out.print(integer + " ");//1 2 3
        }
    }
}
7.6.2.2HashSet存储自定义类型的元素
import java.util.HashSet;

public class Main05 {
    public static void main(String[] args) {
        /**
         * HashSet存储自定义类型的元素
         * 要求同名同学号的人视为同一个人,只存储一次
         *必须重写hashCode方法和equals方法
         */
        HashSet set=new HashSet<>();
        Person p1=new Person("小明",110);
        Person p2=new Person("小明",110);
        Person p3=new Person("小华",111);
        set.add(p1);
        set.add(p2);
        set.add(p3);
        System.out.println(p1.hashCode());//23458864
        System.out.println(p2.hashCode());//23458864
        System.out.println(p3.hashCode());//23310065
        System.out.println(p2.equals(p1));//true
        System.out.println(set);//[Person{name='小华', id=111}, Person{name='小明', id=110}]
    }
}

import java.util.Objects;

public class Person {
    private  String name;
    private int id;

    public Person() {
    }

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

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;
        Person person = (Person) o;
        return getId() == person.getId() &&
                getName().equals(person.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getId());
    }

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

7.6.3 hasCode()方法

public class Main03 {
    public static void main(String[] args) {
        Person p1=new Person();
        Person p2=new Person();
        System.out.println(p1.hashCode());//2125039532
        System.out.println(p2.hashCode());//312714112
        /**
         * toString方法的源码:
         * return    getClass().getName()+"@"+Integer.toHexString(hashCode);
         */
        System.out.println(p1);//Set接口.Person@7ea987ac
        System.out.println(p2);//Set接口.Person@12a3a380
        /**
         * 相同字符串的哈希值相同
         * "重地","童话"的哈希值也相同(都是1179395)
         */
        System.out.println("重地".hashCode());//1179395
        System.out.println("通话".hashCode());//1179395

    }
}

7.6.4 LinkedHashSet集合

底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表,记录元素的存储顺序,保证元素有序

import java.util.HashSet;
import java.util.LinkedHashSet;

public class Main06 {
    public static void main(String[] args) {
        HashSet set =new HashSet<>();
        set.add("abc");
        set.add("bcd");
        set.add("www");
        set.add("123");
        System.out.println(set);//[bcd, 123, abc, www] (无序,不允许重复)
        LinkedHashSet linked =new LinkedHashSet<>();
        linked.add("abc");
        linked.add("bcd");
        linked.add("www");
        linked.add("123");
        System.out.println(linked);//[abc, bcd, www, 123] (有序,不允许重复)
    }
}

7.7 可变参数

数据类型确定但参数个数不确定可用可变参数

注意

1.一个方法的参数列表只能有一个可变参数

2.如果方法参数有多个,那么可变参数必须写在参数列表末尾

import java.util.Objects;

public class Main0 {
    public static void main(String[] args) {
        int i = add(10, 20, 30);
        System.out.println(i);//60
    }

    private static int add(int... arr) {
        System.out.println(arr.length);
        int sum = 0;
        for (int i : arr) {
            sum += i;
        }
        return sum;
    }
    private static void obj(Objects...arr){
         //可变参数的终极写法
    }
}

7.8 集合工具类Collectiongs常用方法

1往集合中添加元素Collections.addAll()

2.将集合中元素的顺序打乱 Collections.shuffle()

import java.util.ArrayList;
import java.util.Collections;

public class Main01 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        Collections.addAll(list, "a", "b", "c", "d");
        System.out.println(list);//[a, b, c, d]
        Collections.shuffle(list);
        System.out.println(list);//[c, a, b, d]
    }
}

3.将集合元素按照默认规则排序 Collections.sort()

import java.util.ArrayList;
        import java.util.Collections;

public class Main01 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        Collections.addAll(list,3,2,1,5,8);
        System.out.println(list);//[3, 2, 1, 5, 8]
        Collections.sort(list);
        System.out.println(list);//[1, 2, 3, 5, 8]
    }
}

4.将集合元素按照自定义规则排序(重写compareTo方法)

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    @Override
    public int compareTo(Person o) {
        return this.getAge() - o.getAge();//升序   倒过来则为降序
    }
}


import java.util.ArrayList;
import java.util.Collections;

public class Main {
    public static void main(String[] args) {
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("张三",18));
        list.add(new Person("李四",20));
        list.add(new Person("王五",15));
        System.out.println(list);
        Collections.sort(list);
        System.out.println(list);
    }
}

5.将集合元素按照自定义规则排序重写Comparator方法

public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        ArrayList list = new ArrayList<>();
        list.add(new Person("张三",18));
        list.add(new Person("李四",20));
        list.add(new Person("王五",15));
        System.out.println(list);
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println(list);
    }
}

注意

1.如果年龄相同则那个先输入集合则排在前面

2.可以用if组合如果age相同则用其他变量排序

7.9 Map集合

java.util.Map集合

1.Map集合的特点:

①:Map集合是一个双列集合,一个集合元素包含两个值(一个是key,一个是value)

②:Map集合中的元素,key和value的数据类型可以相同也可以不同

③:May集合中的元素,key是不允许重复的,value是可以重复的

④:Map集合中的元素,key和value集合是一一对应的

java.util.HashMap集合

2.HashMap集合的特点

①:HashMap集合底层是哈希表:查询速度快

②:HashMap集合是一个无序集合,存储元素和取出元素可能不一致

java.util.LinkedHashMap集合 extends HashMap集合

3.LinkedHashMap集合的特点:

①:LinkedHashMap集合底层是哈希表+链表(保证迭代顺序)

②:LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的

7.9.1 Map集合中常用方法

public V put(K key, V value):把指定键与值添加到Map集合中

返回值V:
①:存储值对的时候,key不重复,返回值v是null
②:存储值对的时候,key重复,会使用新的value替换map中重复的value
,返回被替换的value值
public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的

返回值V:
key存在,v返回被删除值
key不存在,v返回null
public V get(Object key):根据指定的键,在Map集合中获取对应的值
//返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
public boolean containsKey(Object key):如果此映射包含指定键的映射关系,则返回 true,否则返回false
public boolean containsValue(Object value):如果此映射将一个或多个键映射到指定值,则返回 true,否则返回false。

import java.util.HashMap;
import java.util.Map;

public class Main01 {
    public static void main(String[] args) {
        show01();//put方法的使用
        show02();//remove方法的使用
    }

    private static void show01() {
        Map map = new HashMap<>();
        String v1 = map.put("小明", "小华1");
        System.out.println(v1);//null
        System.out.println(map);//{小明=小华1}
        String v2 = map.put("小明", "小华2");
        System.out.println(v2);//小华1
        System.out.println(map);//{小明=小华2}
        map.put("小王", "小黑");
        map.put("小龙", "小云");
        System.out.println(map);//{小龙=小云, 小明=小华2, 小王=小黑}
    }

    private static void show02() {
        Map map = new HashMap<>();
        map.put("小明", 1);
        map.put("小黑", 2);
        map.put("小华", 3);
        //remove方法
        System.out.println(map);//{小明=1, 小华=3, 小黑=2}
        Integer v1 = map.remove("小明");
        System.out.println(v1);//1
        System.out.println(map);//{小华=3, 小黑=2}
        System.out.println(map.remove("小云"));//null
        //get方法
        System.out.println(map.get("小黑"));//2
        System.out.println(map.get("小林"));//null
        //containsKey方法
        System.out.println(map.containsKey("小黑"));
    }
}

7.9.2 Map集合遍历

  • 1.通过键找值:

​ Set keySet()返回此映射中包含的键的Set视图

实现步骤

​ ①:使用Map集合中的方法keySet(),把Map集合所有的key取出来,存储到Set集合中

​ ②:遍历Set集合,获取Map集合中的每一个key

​ ③:通过Map集合中的方法get(Key),通过key找到value

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Main02 {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("小明", 1);
        map.put("小黑", 2);
        map.put("小华", 3);
        Set set = map.keySet();
        Iterator it = set.iterator();
        while (it.hasNext()) {
            String key = it.next();
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
        System.out.println("=====================");
        for (String key : set) {
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
    }
}
  • 2.使用Entry对象遍历
Set> entrySet()//返回此映射中包含的映射关系Set视图

实现步骤:

​ 1.使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中

​ 2.遍历Set集合,获取每一个Entry对象

​ 3.使用Entry对象中的getKey()和getValue()获取键和值

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Main02 {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("小明", 1);
        map.put("小黑", 2);
        map.put("小华", 3);
        Set> set=map.entrySet();
        Iterator> it = set.iterator();
        while (it.hasNext()){
            Map.Entryentry=it.next();
            String key=entry.getKey();
            Integer value=entry.getValue();
            System.out.println(key+"="+value);
        }
        System.out.println("=================");
        for (Map.Entry entry : set) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }
}

7.10 HashMap存储自定义类型键值

Map集合保证key是唯一的:作为key的元素,必须重写hashCode方法和equals方法,以保存key唯一

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Main03 {
    public static void main(String[] args) {
        show01();
        show02();
    }

    private static void show01() {
        HashMapmap=new HashMap<>();
        map.put("北京",new Student("张三",18));
        map.put("上海",new Student("李四",19));
        map.put("广州",new Student("王五",20));
        map.put("北京",new Student("赵六",18));
        Set set=map.keySet();
        for(String key:set){
            Student value=map.get(key);
            System.out.println(key+"-->"+value);
            /*上海-->Student{name='李四', age=19}
            广州-->Student{name='王五', age=20}
            北京-->Student{name='赵六', age=18}*/
        }
    }

    private static void show02() {
        /**
         * 
         */
        HashMap map=new HashMap<>();
        map.put(new Student("小明",18),"深圳");
        map.put(new Student("小华",18),"广州");
        map.put(new Student("小明",18),"珠海");
        map.put(new Student("小虎",18),"汕尾");
        Set> set=map.entrySet();
        for (Map.Entry entry:set){
            Student key=entry.getKey();
            String value=entry.getValue();
            System.out.println(key+"-->"+value);
            /*
            Student{name='小明', age=18}-->珠海
			Student{name='小华', age=18}-->广州
			Student{name='小虎', age=18}-->汕尾
            */
        }
    }
}

**Hashtable集合:**Hashtable不允许存储空值空键

7.11 添加方法的优化of方法

List接口,Set接口,Map接口:里面增加了一个静态方法of,可以给集合一次性添加多个元素

使用前提:

当集合中存储的元素个数已近确定了,不再改变时使用

注意:①:of方法只适用于List接口,Set接口,Map接口

②:of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,否则会抛出异常

③:Set接口和Map接口在调用of方法的时候,不能有重复元素,否则会抛出异常

import java.util.List;
import java.util.Set;

public class Main02 {
    public static void main(String[] args) {
        List list=List.of("a","b","c","a");
        System.out.println(list);//[a,b,c,a]
        Set set=Set.of("a","b","c");
        System.out.println(set);//[a,b,c]
    }
}

第八章

8.1 泛型

泛型是一种未知的数据类型,当不知道使用什么数据类型的时候可以使用泛型,泛型也可以看成是一个变量,用来接收数据类型

E e:Element元素

T t:Type类型

含有泛型的类

public class GenericClass {
    private E number;

    public E getNumber() {
        return number;
    }

    public void setNumber(E number) {
        this.number = number;
    }
}
public class Main {
    public static void main(String[] args) {
        //不要使用泛型默认为Object类型
        //泛型使用String类型
        GenericClass gc = new GenericClass<>();
        gc.setNumber("小明");
        System.out.println(gc.getNumber());
        //泛型使用Integer类型
        GenericClass gc1 = new GenericClass<>();
        gc1.setNumber(1);
        System.out.println(gc1.getNumber());
    }
}

含有泛型的方法

定义格式:修饰符<代表泛型的变量>返回值类型 方法名(参数){…}

public class Main {
    public static void main(String[] args) {
        method("name");
        method(3);
        method('中');
    }
    public static <E> void method(E e){
        System.out.println(e);
    }
}

含有泛型的接口

使用方式1.定义接口的实现类,指定接口泛型

2.接口使用什么泛型,实现类就使用什么泛型,类跟着接口走,相当于定义了一个含有泛型的类,创建对象时确定泛型的类型

public interface GenericInterface {
    public abstract void method(I i);
}
public class GenericInterfaceImpl implements GenericInterface {
    @Override
    public void method(String s){
        System.out.println(s);
    }
}
public class GenericInterfaceImpl2 implements GenericInterface {
    @Override
    public void method(I i) {
        System.out.println(i);
    }
}
public class Main {
    public static void main(String[] args) {
        GenericInterfaceImpl impl=new GenericInterfaceImpl();
        impl.method("字符串");
        GenericInterfaceImpl2 impl2=new GenericInterfaceImpl2<>();
        impl2.method(5);
    }
}

泛型通配符

定义一个方法能遍历所有类型的ArrayList集合,这时不知道集合使用什么数据类型,可以用泛型通配符?来接收数据类型

8.2 多线程

8.2.1 多线程介绍

1.并发和并行

并发:交替执行

并行:同时执行

2.线程与进程

进程:是指一个内存中运行的应用程序,每个进程都有独立的内存空间,一个应用程序可以同时运行多个进程,是系统运行程序的基本单位

线程:是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程的实际运作单位

8.2.2 多线程的创建1

  • 创建多线程程序的第一种方式:创建Thread类的子类

java.lang.Thread类:是描述线程的类,想要实现多线程程序,就必须继承Thread类

实现步骤:

​ ①:创建一个Thread类

​ ②:在Thread类的子类中重写Thread类中的run方法,设置线程任务

​ ③:创建Thread类的子类对象

​ ④:调用Thread类中的方法start方法,开启新的线程,执行run方法

​ void start()使该线程开始执行,Java虚拟机调用该线程的run方法。

​ 结果是两个线程并发地运行;当前线程(从调用返回给start方法)和另 一个线程(执行其run方法)

​ 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重 新启动

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("run"+i);
        }
    }
}
public class Main01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("main" + i);
        }
    }
}//执行结果为抢占式,随机性打印

8.2.3 Thread常用方法

  • public final String getName()返回该线程的名称。
  • public static Thread currentThread()返回对当前正在执行的线程对象的引用。
  • public final void setName(String name)改变线程名称,使之与参数 name 相同。
  • public static void sleep(long millis)throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
public class Main01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        new MyThread().start();
    }
}
public class MyThread extends Thread {

    @Override
    public void run() {
        //获取线程名称
        String name = getName();
        System.out.println(name);//Thread-0
        //获取线程名称
        System.out.println(Thread.currentThread().getName());//Thread-0
        //返回对当前正在执行的线程对象的引用。 
        System.out.println(Thread.currentThread());//Thread[Thread-0,5,main]
    }

}

设置线程名称

//第一种方式(setName)
public class Main01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.setName("线程1");
        mt.start();
    }
}
public class MyThread extends Thread {

    @Override
    public void run() {
        String name = getName();
        System.out.println(getName());
    }
}
//第二种方式
public class Main01 {
    public static void main(String[] args) {
        new MyThread("线程1").start();//线程1
    }
}
public class MyThread extends Thread {

    public MyThread() {
    }

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


    @Override
    public void run() {
        String name = getName();
        System.out.println(getName());
    }
}

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

public class Main02 {
    public static void main(String[] args) {
        //模拟秒表
        for (int i = 1; i <= 60; i++) {
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

8.2.4 多线程的创建2(实现Runnable接口)

实现步骤

1.创建一个Runnable接口的实现类

2.在实现类中重写Runnable接口的run方法,设置线程任务

3.创建一个Runnable接口的实现类对象

4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象

5.调用Thread类中的start方法,开启新的线程执行run方法

常用的构造方法

Thread(Runnable target)//分配新的 Thread 对象。
Thread(Runnable target, String name)//分配新的 Thread 对象。 并修改线程名字
public class Main {
    public static void main(String[] args) {
        //3.创建一个Runnable接口的实现类对象
        RunnableImpl run=new RunnableImpl();
        //4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t=new Thread(run,"线程");
        //5.调用Thread类中的start方法,开启新的线程执行run方法
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}
public class RunnableImpl implements Runnable{//1.创建一个Runnable接口的实现类
    @Override
   // 2.在实现类中重写Runnable接口的run方法,设置线程任务
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"-->"+i);
        }
    }
}

实现Runnable接口创建多线程程序的好处

1.避免了单继承的局限性:一个类只能继承一个类,类继承了Thread类就不能继承其他类

​ 实现了Runnable接口,还可以继承其他类,实现其他接口

2.增强了程序的拓展性,降低了程序的耦合性

实现Runnable接口的方式,把设置线程任务和开启线程进行了分离

​ 实现类中,重写了Run方法;用来设置线程任务

​ 创建Thread类对象,调用start方法:用来开启新线程

3.可使多个线程共享相同资源,以达到资源共享的目的

8.2.5 线程安全问题

卖票问题

1.同步代码块(synchronized)

解决多线程安全问题,多个线程执行到代码块只能一个线程进入,等执行完毕其他线程才能执行

public class Main01 {
    public static void main(String[] args) {
        RunnableImpl run=new RunnableImpl();
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        Thread t3=new Thread(run);
        t1.start();
        t2.start();
        t3.start();
    }
}
public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private static int ticket = 100;
    //创建一个锁对象
    Object obj = new Object();

    //设置线程任务,买票
    @Override
    public void run() {
        //设置循环,让卖票重复进行
        while (true) {
            //同步代码块
            synchronized (obj) {
                //判断票是否存在
                if (ticket > 0) {
                    //提高代码安全性问题,让程序休眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket);
                    ticket--;
                }else break;

            }
        }
    }
}
2.同步方法

定义方法格式:修饰符 synchronized 返回值类型 方法名(参数列表)

public class RunnableImpl implements Runnable {
    //定义一个多个线程共享的票源
    private static int ticket = 100;
    //创建一个锁对象
    Object obj = new Object();

    //设置线程任务,买票
    @Override
    public void run() {
        //设置循环,让卖票重复进行
        while (true) {
            method();
        }
    }

    public synchronized void method() {
        if (ticket > 0) {
            //提高代码安全性问题,让程序休眠
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket);
            ticket--;
        }
    }
}
public class Main01 {
    public static void main(String[] args) {
        RunnableImpl run=new RunnableImpl();
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        Thread t3=new Thread(run);
        t1.start();
        t2.start();
        t3.start();
    }
}
3.使用Lock锁

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

常用方法:

public void lock()  //获取锁。

public void unlock()  //释放锁。 

使用步骤:

​ 1.在成员位置创建一个ReentrantLock对象

​ 2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁

​ 3.在可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class RunnableImpl implements Runnable {
    private static int ticket = 100;
    Object obj = new Object();
    Lock l = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            l.lock();
            if (ticket > 0) {
                //提高代码安全性问题,让程序休眠
                try {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName() + "正在卖票" + ticket);
                    ticket--;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    l.unlock();
                }
            }
        }
    }
}
public class Main01 {
    public static void main(String[] args) {
        RunnableImpl run=new RunnableImpl();
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        Thread t3=new Thread(run);
        t1.start();
        t2.start();
        t3.start();
    }
}

8.3线程状态

线程状态 导致状态发生条件
New状态 线程刚被创建,但是并未启动。还没调用start方法
Runnable(可运行状态) 线程可以在Java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,这取决于操作系统处理器
Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而该对象锁被其他线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将编程Runnable状态
Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后不能自动唤醒,必须等待另一个线程调用notify或者notifyAll方法才能唤醒
Timed Waiting(计时状态) 同Waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者收到唤醒通知,带有超时参数的方法有Thread.sleep,Object.wait。
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获异常终止了run方法而死亡

8.3.1Waiting(无限等待状态)

wait方法和notify方法

/**买卖包子案例
 * 两个线程必须使用同步代码块包裹起来,保证等待和唤醒只有一个在执行
 * 同步使用的锁对象必须保证唯一
 * 只有锁对象才能调用wait和notify方法
 */
public class Main01 {
    public static void main(String[] args) {
        //创建锁对象,保证唯一
        Object obj = new Object();
        new Thread() {
            @Override
            public void run() {
                synchronized (obj) {
                    System.out.println("告知老板要的包子的种类和数量");
                    //调用wait方法,放弃cpu的执行,进入Waiting状态
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("包子已经做好");
                }
            }
        }.start();
        //创建一个老板线程
        new Thread() {
            @Override
            public void run() {
                //花了5秒做包子
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj) {
                    System.out.println("包子做好,告知消费者");
                    obj.notify();
                }
            }
        }.start();
    }
}

8.3.2Time Waiting(计时状态)

·进入TimeWaiting有两种方式:

​ 1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态

​ 2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡 醒进入到Runnable/Blocked状态

·唤醒方法:

​ 1.void notify()唤醒再次对象监视器上等待的单个线程

​ 2.void notifyAll()唤醒在此监视器等待的所有线程

8.3.3线程通信

8.3.4 线程池

线程池:JDK1.5之后提供的

Java.util.concurrent.Executors:线程池的工厂类,用来生成线程池

Executors类中的静态方法:

static ExecutorService newFixedThreadPool(int nThreads)
    //创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
    /**
    *参数int nThreads:创建线程池中包含线程的数量
    *返回值ExecutorService接口,返回值是ExecutorService接口的实现类对象,
    *我们可以用ExecutorService接口接收
    *Java.util.concurrent.ExecutorService:线程池接口
    *用来从线程池中获取线程,调用start方法,执行线程任务
    * submit(Runnable task)提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
    *  void shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务 
    **/

线程池的使用步骤:

​ 1.使用线程池的工厂类Executors里面的静态方法newFixedThreadPool生产一个指定线程数量的线程池

​ 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务

​ 3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法

​ 4.调用ExecutorService中的方法shut down销毁线程池(不建议执行)

//  2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
public class RunnableImpl implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "创建了一个新的线程执行");
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        //1.使用线程池的工厂类Executors里面的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es= Executors.newFixedThreadPool(2);
        //3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        //线程会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
        es.submit(new RunnableImpl());//pool-1-thread-2创建了一个新的线程执行
        es.submit(new RunnableImpl());//pool-1-thread-1创建了一个新的线程执行
        es.shutdown();//关闭线程池,不能再调用submit否则会抛出异常
    }
}

8.4 Lambda表达式

8.4.1Lambda表达式与传统方法的对比

public class Main {
    public static void main(String[] args) {
        //使用匿名内部类方法实现多线程
        new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println(Thread.currentThread().getName()+"新线程创建了");
            }
        }).start();
        //使用Lambda表达式,实现多线程
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"新线程创建了");
        }).start();
    }
}
()->{System.out.println("新线程创建了");
     /**
     *前面的一对小括号即run方法的参数(无),代表不需要任何条件
     *中间的一个箭头代表将前面的参数传递给后面的代码
     *后面的输出语句即业务逻辑代码
     */
 格式:
     (参数列表)->{一些重写方法的代码}
     ->:传递的意思,把参数传递给方法体{}
     {}:重写接口的抽象方法的方法体

8.4.2使用Lambda表达式对对象进行排序

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        Person[]arr={
                new Person("迪丽热巴",18),
                new Person("古力娜扎",20),
                new Person("马尔扎哈",19)
        };
        //对年龄进行排序
       /**使用传统方法
        * Arrays.sort(arr, new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });*/
       /**使用Lambda表达式*/
       Arrays.sort(arr,(Person o1,Person o2)->{ return o1.getAge()-o2.getAge();});
        /**优化省略Lambda*/
         Arrays.sort(arr,(o1,o2)->{o1.getAge()-o2.getAge());
        for(Person p:arr){
            System.out.println(p);
        }
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

8.4.3 Lambda表达式的使用前提

1.使用Lambda必须具有接口,且要求接口有且只有一个抽象方法

2.使用Lambda必须具有上下文推断,也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。(有且只有一个抽象方法的接口称为函数式接口

8.4.4 Lambda表达式是可省略的

可省略内容:

​ 1.(参数列表):括号中参数列表中的数据类型可以省略不写

​ 2.(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略

​ 3.{一些代码}:如果{}中的代码只有一行,那么无论是否有返回值,都可以省略{},return,分号

注意:要省略{},return,分号必须一起省略

8.5 File类

java.io.File类

文件和目录路径名的抽象表示形式

Java把电脑中的文件和文件夹(目录)封装为了一个File类,我们可以使用File类对文件和文件夹进行操作

我们可以使用File类的方法:(创建一个文件/文件夹、删除文件/文件夹、获取文件/文件夹、判断文件/文件 夹是否存在、对文件夹进行遍历、获取文件大小)

File是一个与操作系统无关的类,任何操作系统都可以使用这个类中的方法

(file文件、directary文件夹/目录、path路径)

8.5.1 File的四个静态成员变量

  • static String pathSeparator与系统有关的路径分隔符,为了方便,它被表示为一个字符串。
  • static char pathSeparatorChar与系统有关的路径分隔符。
  • static String separator与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
  • static char separatorChar与系统有关的默认名称分隔符。
import java.io.File;

public class Main {
    public static void main(String[] args) {
        /**操作系统:路径不能写死了
         * C:\Program Files\a.txt   windows
         * C:/Program Files/a.txt   Linux
         * C:"+File.separator+"Program Files"+File.separator+"a.txt 
         */
        System.out.println(File.pathSeparator);//路径分隔符  windows:分号;  Linux:冒号:
        System.out.println(File.separator);//文件名称分隔符 windows:反斜杠:\  Linux:正斜杠/
        System.out.println(File.pathSeparatorChar);
        System.out.println(File.separatorChar);
    }
}

路径

绝对路径:是一个完整的路径(以盘符开始的路径 F:\AI学习资料\a.txt)

相对路径:是一个简化的路径(相对指的是相对于当前项目的根目录)

​ 如果使用当前项目的根目录,路径可以简化书写(F:\AI学习资料\a.txt简化为a.txt(可以省略项目 的根目录))

**注意:**1.路径是不区分大小写的 2.路径中的文件名分隔符windows使用反斜杠,反斜杠是转义字符,两个反 斜杠代表普通的一个反斜杠

8.5.2 File类的构造方法

构造方法

File(String pathname) //通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
File(String parent, String child) //根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。   
File(File parent, String child)//根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。  
import java.io.File;

public class Main {
    public static void main(String[] args) {
        show01();
        show02("d:\\", "a.txt");
        show03();
    }

    private static void show01() {
        /**
         * File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
         * 参数:String pathname:字符串的路径名称
         * 路径可以是以文件结尾,也可以是以文件夹结尾
         * 路径可以是存在也可以是不存在
         * 创建File对象,只是把字符串路径封装为File对象,不考虑路径的真假情况
         */
        File f1 = new File("F:\\untitled\\src\\File\\a.txt");
        System.out.println(f1);//F:\\untitled\src\File\a.txt
        /**重写了toString方法*/
    }

    private static void show02(String parent, String child) {
        /**File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
         *参数:把路径分成了两个部分
         * String parent:父路径    String child子路径
         * 好处:父路径和子路径,可以单独写,使用起来灵活,父路径和子路径都可以变化
         */
        File file = new File(parent, child);
        System.out.println(file);//d:\a.txt
    }

    private static void show03() {
        /**File(File parent, String child)根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
         * 参数:把路径分成了两个部分
         * String parent:父路径    String child子路径
         * 好处:父路径和子路径,可以单独写,使用起来灵活,父路径和子路径都可以变化
         *      父路径是File类型,可以使用File的方法对路径进行一些操作,再使用路径创建对象
         */
        File parent = new File("d:\\");
        File file = new File(parent, "hello.java");
        System.out.println(file);//d:\hello.java
    }
}

8.5.3 File类常用方法

public String getAbsolutePath()//返回此抽象路径名的绝对路径名字符串。
public String getName() //返回由此抽象路径名表示的文件或目录的名称。
public String getPath()//将此抽象路径名转换为一个路径名字符串(文件中的toString方法就是返回getPath方法)
public long length()//返回由此抽象路径名表示的文件的长度 (文件夹是没有大小概念的,不能获取文件夹大小)
import java.io.File;

public class Main01 {
    public static void main(String[] args) {
        File file = new File("D:///aaabbb.java");
        System.out.println("文件的绝对路径" + file.getAbsolutePath());//文件的绝对路径D:\aaa\bbb.java
        System.out.println("文件的构造路径" + file.getPath());//文件的构造路径D:\aaa\bbb.java
        System.out.println("文件名称" + file.getName());//文件名称bbb.java
        System.out.println("文件长度" + file.length() + "字节");//文件长度0字节

        File file1 = new File("d:/aaa");
        System.out.println("目录的绝对路径" + file.getAbsolutePath());//目录的绝对路径D:\aaa\bbb.java
        System.out.println("目录的构造路径" + file.getPath());//目录的构造路径D:\aaa\bbb.java
        System.out.println("目录名称" + file.getName());//目录名称bbb.java
        System.out.println("目录长度" + file.length() + "字节");//目录长度0字节
    }
}

8.5.4 判断功能的方法

boolean exists()//测试此抽象路径名表示的文件或目录是否存在。
boolean isDirectory()//测试此抽象路径名表示的文件是否是一个目录。
boolean isFile()//测试此抽象路径名表示的文件是否是一个标准文件。
import java.io.File;

public class Main01 {
    public static void main(String[] args) {
        File file = new File("C:\\Program Files\\Java\\a.txt");
        File file1 = new File("C:\\Program Files");
        //判断是否存在
        System.out.println(file.exists());//true
        System.out.println(file1.exists());//true
        //判断是文件还是目录
        System.out.println("file是文件?" + file1.isFile());//file是文件?false
        System.out.println("file是目录?" + file1.isDirectory());//file是目录?true  目录:文件夹
    }
}

8.5.5 创建删除功能的方法

boolean createNewFile()//当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
boolean delete()//删除此抽象路径名表示的文件或目录。
boolean mkdir()//创建此抽象路径名指定的目录。
boolean mkdirs()//创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
import java.io.File;
import java.io.IOException;

public class Main01 {
    public static void main(String[] args) throws IOException {
        //文件创建
        File file = new File("F:\\JavaFile\\a.txt");
        System.out.println("是否存在" + file.exists());
        System.out.println("是否创建" + file.createNewFile());
        System.out.println("是否存在" + file.exists());

        //目录创建
        File file1 = new File("F:\\JavaFile\\newDir");
        System.out.println("是否存在" + file1.exists());
        System.out.println("是否创建" + file1.mkdir());
        System.out.println("是否存在" + file1.exists());

        //创建多级目录
        File file2 = new File("F:\\JavaFile\\newDira\\newDirb");
        System.out.println("是否存在" + file2.exists());
        System.out.println("是否创建" + file2.mkdirs());
        System.out.println("是否存在" + file2.exists());

        //删除文件或目录
        System.out.println("是否删除" + file.delete());
        System.out.println("是否删除" + file1.delete());
        System.out.println("是否删除" + file2.delete());
    }
}

8.5.6 File类遍历(文件夹)目录功能

File[] listFiles()//返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
File[] listFiles(FileFilter filter)//返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 

注意: list方法和listFiles方法遍历的是构造方法中给出的目录

如果构造方法中给出的目录的路径不存在,会抛出空指针异常

如果构造方法中给出的路径不是一个目录,也会抛出空指针异常

import java.io.File;
import java.io.IOException;

public class Main01 {
    public static void main(String[] args) throws IOException {
        //把文件名保存为字符串
        File file = new File("F:\\JavaFile");
        String[] arr = file.list();
        for (String filename : arr) {
            System.out.println(filename);
        }

        //把文件/文件夹封装为File对象,多个File存储到File数组中
        File file1 = new File("F:\\JavaFile");
        File[] files = file.listFiles();
        for (File filename : files) {
            System.out.println(filename);//打印出绝对路径
        }
    }
}

8.5.6 FileFilter过滤器的原理和使用

在File类中有两个和ListFiles重载的方法,方法参数传递的就是过滤器

  • File[] listFiles(FileFilter filter)返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。

  • Java.io.FileFilter接口:用于抽象路径名(File对象)的过滤器

    作用:用来过滤文件(File对象)

    抽象方法:用来过滤文件的方法

  • boolean accept(File pathname)测试指定抽象路径名是否应该包含在某个路径名列表中。

    参数:File pathname使用ListFiles方法遍历目录,得到的每一个文件对象

  • File[] listFiles(FilenameFilter filter)返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。

  • Java.io.FilenameFilter接口:实现此接口的类实例可用于过滤文件名

    作用:用于过滤文件名称

    抽象方法:用来过滤文件的方法

  • boolean accept(File dir, String name)测试指定文件是否应该包含在某一文件列表中。

    参数:File dir:构造方法中传递的被遍历目录

    String name:使用ListFiles方法遍历目录,获取的每一个文件/文件夹的名称

    注意:两个过滤器接口是没有实现类的,需要我们自己写实现类,重写过滤的方法accept,在方法中自己定义过滤的规则

8.6 IO流

8.6.1字节流

1.常用方法
  • Java.io.OutputStream(字节输出流):此抽象类是表示输出字节流的所有类的超类
    定义了一些共性的方法:

  • void close() 关闭此输出流并释放与此流有关的所有系统资源。

  • void flush()刷新此输出流,并强制将所有已缓冲的输出字节写入该流中。

  • void write(byte[] b)将 b.length 个字节写入此输出流。

  • void write(byte[] b, int off, int len)将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

  • void write(int b)将指定 byte 写入此输出流。
    public class FilterOutputStream extends OutputStream

  • FilterOutputStream:文件字节输出流

    作用:把内存中的数据写入到硬盘的文件中
    FileOutputStream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流。
    FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    参数:写入数据的目的
    String name:目的地是一个文件的路径
    File file:目的地是一个文件
    构造方法的作用:
    1.创建一个FilterOutputStream
    2.会根据构造方法中传递的文件/文件路径,创建空文件
    3.会把FilterOutputStream对象指向创建好的文件

import java.io.FileOutputStream;
import java.io.IOException;

public class Main02 {
    public static void main(String[] args) throws IOException {
        /*write(方法的使用)*/
        //1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("F:\\JavaFile\\a.txt");
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        fos.write(97);//会用ASCII码表转化为字符
        fos.write('a');

        byte[] bytes = {65, 66, 67, 68, 69};
        byte[] bytes1 = "你好".getBytes();
        fos.write(bytes);
        fos.write(bytes1);
        fos.write(bytes1, 0, 3);
        //3.释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
        fos.close();
    }
}
2.续写和换行

续写

FileOutputStream(File file, boolean append)创建一个向指定 File 对象表示的文件中写入数据的文件 输出流。
FileOutputStream(String name, boolean append)创建一个向具有指定 name 的文件中写入数据的输出 文件流。
**参数:**File file,String name:写入数据的目的地
boolean append:追加写开光 -->true:创建对象不会覆盖原文件,继续在文件末尾追加写数据,false:创 建一个新文件,覆盖原文件

换行

写换行符号:windows:\r\n Linux:/n mac:/r

import java.io.FileOutputStream;
import java.io.IOException;

public class Main02 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("F:\\JavaFile\\a.txt", true);
        for (int i = 0; i < 10; i++) {
            fos.write("这是续写内容".getBytes());
            fos.write("\r\n".getBytes());
        }
        fos.close();
    }
}

8.6.2 字节输入流

public abstract class InputStream extends Object implements Closeable此抽象类是表示字节输入流的所有类的超类。
abstract int read()从输入流中读取数据的下一个字节。
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
void close()关闭此输入流并释放与该流关联的所有系统资源。

public class FilterInputStream extends InputStream
FilterInputStream:文件字节流

作用:把硬盘中文件的数据,读取到内存中使用
构造方法:FilterInputStream(String name)
FilterInputStream(File file)
构造方法的作用:
1.会创建一个FilterInputStream对象
2.会把FilterInputStream对象指定构造方法中要读取的文件

  • 一次读取一个字节
 import java.io.FileInputStream;
 import java.io.IOException;
 
 public class Main02 {
     public static void main(String[] args) throws IOException {
         FileInputStream fis = new FileInputStream("F:\\JavaFile\\b.txt");
         int len = 0;
         while ((len = fis.read()) != -1) {//文件读完会读介绍标记为-1
             System.out.println((char) len);
         }
         fis.close();
     }
 }
  • 一次读取多个字节
/**int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
 * 方法的参数byte[]起到缓冲作用,存储每次读取到的多个字节
 * 数组的长度一般定义为1024(1kb)或者1024的整数倍
 * 2.方法的返回值int是每次读取的有效字节个数
 *
 * String类的构造方法
 *      String(byte[] bytes):把字节数组转换为字符串
 *      String(byte[] bytes,int offset,int length)把字节数组的一部分转换为字符串offset:数组的开始索引,length:转换的字节个数
 */

import java.io.FileInputStream;
import java.io.IOException;

public class Main02 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("F:\\JavaFile\\b.txt");
        byte[] bytes=new byte[1024];
        int len =fis.read(bytes);
        System.out.println(len);
        System.out.println(new String(bytes,0,len));
        fis.close();
    }
}

8.6.3 文件读写练习

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main02 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("F:\\JavaFile\\b.txt");//读取位置
        FileOutputStream fos=new FileOutputStream("F:\\JavaFile\\newDir\\a.txt");//写入位置
        int len=0;
        byte[] bytes=new byte[1024];
        while ((len =fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }
        fos.close();
        fis.close();
    }
}

8.6.4字符流

  • 当使用字节流读取文本时,若遇到中文字符时可能会出现乱码问题,因为一个中文占用多个字节

    所以Java提供了字符流用于读写数据,专门用于处理文本文件

java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类
共性的成员方法:
    int rad():读取当个字符并返回
    int rad(char[] cbuf):一次读取多个字符,将字符读入数组
    void close():关闭该流并释放与之关联的所以资源
public class FileReader extends InputStreamReader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中
import java.io.FileReader;
import java.io.IOException;

public class Main02 {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("F:\\JavaFile\\a.txt");
        char[]cs=new char[1024];
        int len=0;
        while ((len=fr.read(cs))!=-1){
            System.out.println(new String(cs,0,len));
        }
        fr.close();
    }
}

8.6.5 字符输出流

java.io.Writer:字符输出流,是所有字符输出流的顶层父类,是一个抽象类
共性的成员方法
void close() //关闭流,先刷新。  
void flush() //刷新流。  
String getEncoding() //返回此流使用的字符编码的名称。  
void write(char[] cbuf, int off, int len) //写入字符数组的一部分。  
void write(int c) //写一个字符  
void write(String str, int off, int len) //写一个字符串的一部分。 
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("F:\\JavaFile\\d.txt");
        char[] cs = {'a', 'b', 'b', 'f'};
        fw.write(cs);
        //写完必须经过刷新才能从内存中写人到硬盘中,而方法flush和close都会刷新
        fw.flush();
        fw.write("写入数据");
        fw.write("写入数据的一部分", 5, 3);
        fw.close();
    }
}

8.6.6 使用Properties集合

public class Properties extends Hashtable
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties集合是一个唯一和IO流相结合的集合
    可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
    可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用
 属性列表中每个键及其对应值都是一个字符串。
    Properties集合是一个双列集合,key和value默认都是字符串
    
1.存储数据、遍历
String getProperty(String key) //通过key找到value值,此方法相当于Map集合中的get(key)方法
Object setProperty(String key, String value) //调用 Hashtable 的方法 put。
Set stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
import java.io.*;
import java.util.Properties;
import java.util.Set;

public class Main02 {
    public static void main(String[] args) throws IOException {
        Properties prop=new Properties();
        prop.setProperty("小明","18");
        prop.setProperty("小黑","20");
        prop.setProperty("小林","19");
        Set set =prop.stringPropertyNames();
        for(String key:set){
            String value = prop.getProperty(key);
            System.out.println(key+"-->"+value);
        }
    }
}
2.输出流(持久化写入到硬盘中)store
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
 void store(OutputStream out, String comments)  
 void store(Writer writer, String comments)  
 参数:OutputStream out:字节输出流,不能写入中文
      Writer writer:字符输出流,可以使用中文
      String comments:注释:用来解释说明保存的文件是做什么的
      不能使用中文,会产生乱码,默认是Unicode编码
      一般使用""空字符
import java.io.*;
import java.util.Properties;

public class Main02 {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        prop.setProperty("小明", "18");
        prop.setProperty("小黑", "20");
        prop.setProperty("小林", "19");
        FileWriter fw = new FileWriter("F:\\JavaFile\\d.txt");//字符输出流可以写中文
        prop.store(fw, "save data");
        fw.close();
    }
}
3.输入流
import java.io.*;
import java.util.Properties;
import java.util.Set;

public class Main02 {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        FileReader fw = new FileReader("F:\\JavaFile\\d.txt");
        prop.load(fw);
        Set set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key + "-->" + value);
        }
        fw.close();
    }
}
void load(InputStream inStream)// 从输入流中读取属性列表(键和元素对)。
void load(Reader reader)//按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
参数:InputStream inStream:字节输入流,不能读取含有中文的键值对
     Reader reader: 字符输入流,能读取含有中文的键值对
     注意 :1.存储键值的文件中,键与值默认的连接符号可以用=,空格或其他符号
           2.存储键值的文件中,可以使用#进行注释,被注释的键值不会被读取
           3.存储键值的文件中,键与值默认都是字符串,不用再加引号

8.6.7 缓冲流

1.字节缓存输出流
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
OutputStream out:字节输出流
可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size:指定缓冲流内部缓冲区大小,不指定默认
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
        //BufferedOutputStream的使用
        //1.创建FileOutputStream对象,构造方法中要绑定输出的目的
        FileOutputStream fos = new FileOutputStream("F:\\JavaFile\\a.txt", true);
        //2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3.使用BufferedOutputStream对象中的方法write,把数据写到内存缓冲区
        bos.write("把数据写到内存缓冲区中".getBytes());
        //4.把内存缓冲区的数据刷新到文件中
        bos.flush();
        //5.释放资源
        bos.close();
    }
}
2.字节缓存输入流
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
        //BufferedInputStream的使用
        //1.创建FileInputStream对象,构造方法中要绑定输出的目的
        FileInputStream fis = new FileInputStream("F:\\JavaFile\\a.txt");
        //2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3.使用BufferedInputStream对象中的方法read,读取数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = bis.read(bytes)) != -1) {
            System.out.println(new String(bytes, 0, 66));
        }
        bis.close();
        fis.close();
    }
}
3.字符缓冲输入流
  • 特有的成员方法:void newLine():写入一个行分隔符
public class Main02 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\JavaFile\\a.txt"));
        for (int i = 0; i < 10; i++) {
            bw.write("写入数据");
            bw.newLine();//换行
        }
        bw.flush();
        bw.close();
    }
}
4.字符缓冲输出流
  • 特有成员方法:String readLine():读取一个文本行,读取一行数据(返回值:返回该行字符串,不包含任何终止符,如果已近到达流末尾,则返回null)
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("F:\\JavaFile\\a.txt"));
        for (int i = 0; i < 6; i++) {
            br.readLine();
            System.out.println(br.readLine());
        }
        br.close();
    }
}

8.6.8 转换流

OutputStreamWriter(OutputStream out) //创建使用默认字符编码的 OutputStreamWriter。
OutputStreamWriter(OutputStream out, String charsetName)//创建使用指定字符集的 OutputStreamWriter。
参数:
    OutputStream out:字节输出流,可以用来写转换之后的字节到文件中;
    String charsetName:指定编码表的名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK...
使用步骤:1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
         2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
         3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
         4.释放资源
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
        write_utf_8();
        write_gdk_8();
    }

    private static void write_utf_8() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("F:\\JavaFile\\a.txt"), "UTF-8");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }

    private static void write_gdk_8() throws IOException {
        //1.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("F:\\JavaFile\\b.txt"), "gbk");
        //2.使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        //4.释放资源
        osw.close();
    }
}
InputStreamReader(InputStream in)//创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName)//创建使用指定字符集的 InputStreamReader。

8.6.9 序列化和反序列化

1. 序列化
构造方法:
ObjectOutputStream(OutputStream out) //创建写入指定 OutputStream 的 ObjectOutputStream。
特有的成员方法:
writeObject(Object obj) //将指定的对象写入 ObjectOutputStream。
使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文本中
3.释放资源
/**
 * 序列化和反序列化的时候会抛出NotSerializableException没有序列化异常
 * 类通过实现java.io.Serializable接口以启动其序列化功能,来实现此接口的类无法使其任何状态序列化或反序列化
 * Serializable接口也叫标记型接口
 * 要进行序列化和反序列化的时候,就会检测类上是否有这个标记
 * 当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
 * 没有就会抛出异常
 */
import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException {
     ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\JavaFile\\b.txt"));
        oos.writeObject(new Person("小明", 18));
        oos.close();
    }
}
2.反序列化
构造方法:
ObjectInputStream(InputStream in) //创建从指定 InputStream 读取的 ObjectInputStream。
特有的成员方法:
Object readObject() // 从 ObjectInputStream 读取对象。
使用步骤:
1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject()读取保存对象的文件
3.释放资源
4.使用读取出来的对象
readObject()方法抛出了ClassNotFoundException(class文件找不到异常)
当不存在对象class文件时抛出异常
反序列化前提:
1.类必须实现Serializable
2.必须存在类对应的class文件
import java.io.*;

public class Main02 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\JavaFile\\b.txt"));
        Object o = ois.readObject();
        ois.close();
        System.out.println(o);
        Person p = (Person) o;
        System.out.println(p.getName() + "," + p.getAge());
    }
}
3.瞬态关键字transient

被transient修饰的成员变量不能被序列化

8.6.10打印流

Java.io.printStream extends OutputStream:打印流
特点:1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同,printStream永远不会抛出IOException
3.有特有的方法print,println
void print(任意类型的值)
void println(任意类型的值并换行)
构造方法:
PrintStream(File file):输出的目的地是一个文件
PrintStream(String fileName) :输出的目的地是一个字节输出流
PrintStream(OutputStream out) :输出的目的地是一个文件路径
注意:
如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表(97-->a)
如果使用自己特有的方法print方法写数据,写的数据原样输出
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        PrintStream ps = new PrintStream("F:\\javaFile\\d.txt");
        double a = 2;
        ps.printf("%.2f", a);//控制输出格式
        ps.write(97);//输出为a
        ps.print("HelloWorld");
        ps.close();

        //使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
        PrintStream printStream = new PrintStream("F:\\javaFile\\d.txt");
        System.out.println("这是在控制台输出的语句");
        System.setOut(printStream);
        System.out.println("这是在打印流的目的地中输出");
        printStream.close();
    }
}

第九章

9.1网络编程入门

9.1.1 常用端口号

  • 端口号由两个字节组成范围在0-65535之间,1024之前的端口号我们无法使用,已经被系统分配给已知的网络软件了

1.80端口(网络端口)www.baidu.com:80

2.数据库 MySQL(3306) Oracle(1521)

3.Tomcat(服务器):8080

9.1.2 Socket类

  • Socket类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点

  • 在Java中,提供了两个类用于实现TCP通信协议

1.客户端:Java.net.Socket:类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信

2.服务端:Java.net.ServerSocket类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接

9.1.3 服务器与客户端间的通信

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
    TCP通信的服务器:接收客户端请求,读取客户端发送的数据,读取客户端发送的数据,给客户端回写数据
    表示服务器的类:java.net.ServerSocket:此类实现服务器套接字
    构造方法:
    ServerSocket(int port)//创建绑定到特定端口的服务器套接字。
    服务器必须明确是哪个客户端请求的服务器所以使用accept方法获取到请求的客户端对象Socket
    成员方法:
    Socket accept()监听并接受到此套接字的连接
    服务器实现步骤:
            1.创建服务器ServerSocket对象和系统要指定的端口号
            2.使用ServerSocket对象中的方法accept,获取到请求客户端对象Socket
            3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
            4.使用网络字节输入流InputStream对象中的read方法,读取客户端发送的数据
            5.使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
            6.使用网络字节输出流OutputStream对象中的方法write,给客户端发送数据
            7.释放资源(Socket,ServerSocket)
 */
public class TCPServer {
    public static void main(String[] args) throws IOException {
//        1.创建服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server =new ServerSocket(8888);
//        2.使用ServerSocket对象中的方法accept,获取到请求客户端对象Socket
        Socket socket=server.accept();
//        3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is=socket.getInputStream();
//        4.使用网络字节输入流InputStream对象中的read方法,读取客户端发送的数据
        byte[] bytes=new byte[1024];
        int len=is.read(bytes);
        System.out.println(new String(bytes,0,len));
//        5.使用Socket对象中的方法getOutputStream()获取网络字节输入流OutputStream对象
        OutputStream os =socket.getOutputStream();
//        6.使用网络字节输出流OutputStream对象中的方法write,给客户端发送数据
        os.write("收到谢谢".getBytes());
//        7.释放资源(Socket,ServerSocket)
        socket.close();
        server.close();
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
TCP通信的客户端:向服务器发送连接请求,读取服务器回写的数据
表示客户端的类:
Java.net.Socket:此类实现客户端套接字。套接字是两台机器间通信的端点
套接字:包含了IP地址和端口号的网络单位
构造方法:
Socket(String host, int port)//创建一个流套接字并将其连接到指定主机上的指定端口号。
参数:String host:服务器主机的名称/服务器的IP地址
      int port:服务器的端口号
成员方法:
 InputStream getInputStream()//返回此套接字的输入流。
 OutputStream getOutputStream()//返回此套接字的输出流。
  void close()//关闭此套接字。
  实现步骤:1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
           2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
           3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
           4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
           5.使用网络字节输入流InputStream对象中的read方法,读取服务器回写的数据
           6.释放资源(Socket)
  注意:
  1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
  2.当创建客户端对象Socket的时候,就会去请求服务器,和服务器进行3此握手建立连接通路
  如果服务器没有启动就会抛出异常NoRouteToHostException

 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        Socket socket=new Socket("192.168.43.142",8888);
        //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os =socket.getOutputStream();
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        os.write("你好服务器".getBytes());
        //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is=socket.getInputStream();
        //5.使用网络字节输入流InputStream对象中的read方法,读取服务器回写的数据
        byte[] bytes=new byte[1024];
        int len=is.read(bytes);
        System.out.println(new String(bytes,0,len));
        // 6.释放资源(Socket)
        socket.close();
    }
}

9.1.4 文件上传

  • 在文件上传的过程中会用到read(bytes)读取本地文件,而read读取不到结束标记,无法给服务器发送结束标记,所以客户端和服务器会进入阻塞状态。

    可用void shutdownOutput()禁用此套接字的输出流。使其终止读取

  • 为防止文件名称相同,使其覆盖原文件,可自定义一个文件命名规则,防止命名相同

9.2 函数式接口

函数式接口;有且只有一个抽象方法的接口,称之为函数式接口,当然接口中可以包含其他的方法(默认、静态、私有)

@FunctionalInterface注解

作用:可以检测接口是否为一个函数式接口(是:编译成功,否:编译失败(接口中没有抽象方法,或抽象方法多于一个))

9.2.1 函数式接口的使用

  • 使用:一般可以作为方法的参数和返回值类型

    @FunctionalInterface
    public interface MessageBuilder {
        public abstract String method();
    }
    /*
     使用Lambda优化日志案例
     Lambda的特点:延迟执行
     Lambda的使用前提:必须存在函数式接口
     使用Lambda表达式作为参数传递,仅仅只是把参数传递到showLog方法中
        只有满足条件才会调用MessageBuilder中的方法method才会进行字符串拼接,不会存在性能浪费
     */
    public class Main {
        public static void showLog(int level, MessageBuilder mb) {
            if (level == 1) {
                System.out.println(mb.method());
            }
        }
        public static void main(String[] args) {
            String msg1 = "Hello";
            String msg2 = "World";
            String msg3 = "Java";
            showLog(1, () -> msg1 + msg2 + msg3);
        }
    }
    
  • 如果方法的参数传递的是函数式接口可以使用Lambda表达式进行优化

9.2.2 常用的函数式接口

1.Supplier< T >接口

java.util.function.Supplier< T >接口仅包含一个无参方法:T get()。用来获取一个泛型参数指定类型的对象数据。

Supplier< T >接口被称为生产型接口,指定接口的泛型是什么类型,那么get()方法就会生产什么类型的数据

import java.util.function.Supplier;

public class functionSupplier {
    public static String getString(Supplier sup) {
        return sup.get();
    }

    public static void main(String[] args) {
        //调用getString方法,方法参数是一个函数式接口,所以可以传递Lambda表达式
        String s = getString(() -> {
            return "胡歌";
        });
        System.out.println(s);
    }
}
2.Consumer< T >接口

Java.util.function.Consumer< T >接口则正好与Supplier接口相反,它是消费一个数据

Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据

import java.util.function.Consumer;

public class Main01 {
    public static void method(String name, Consumer consumer) {
        consumer.accept(name);
    }

    public static void main(String[] args) {
        method("赵丽颖", (String name) -> {
            //对传递字符串进行消费
            String reName = new StringBuffer(name).reverse().toString();
            System.out.println(reName);//颖丽赵
        });

    }
}
  • Consumer< T >接口的默认方法:andThen

如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现效果:消费数据的时候,首先做一个操作,然后再做一个操作,实现组合。而这个方法就是Consumer接口中的andThen

作用:需要两个Consumer接口,可以把两个Consumer接口组合到一起,再对数据进行消费

import java.util.function.Consumer;

public class Main01 {
    public static void method(String s, Consumer con1, Consumer con2) {
        //con1.accept(s);
        //con2.accept(s);
        //使用andThen方法,把两个Consumer接口连接在一起,再消费数据
        con1.andThen(con2).accept(s);
    }

    public static void main(String[] args) {
        method("hello", (t) -> {
            System.out.println(t.toUpperCase());
        }, (t) -> {
            System.out.println(t.toLowerCase());
        });
    }
}
  • 例题:格式化打印信息
import java.util.function.Consumer;

public class Main01 {
    public static void method(String[] arr, Consumer con1, Consumer con2) {
        for (String s : arr) {
            //使用andThen方法连接两个Consumer接口,消费字符串
            con1.andThen(con2).accept(s);
        }
    }

    public static void main(String[] args) {
        String[] arr = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
        method(arr, (s) -> {
            //消费方式,对s进行切割
            String name = s.split(",")[0];
            System.out.print("姓名:" + name + ",");
        }, (s) -> {
            String sex = s.split(",")[1];
            System.out.println("性别:" + sex + "。");
        });
    }
}
3.Predicate< T > 接口

作用:对某种数据类型的数据进行判断,结果返回一个boolean值

Predicate接口中包含一个抽象方法:boolean test(T t):用来对指定类型数据进行判断方法

import java.util.function.Predicate;

public class Main02 {
    public static boolean method(String s, Predicate predicate) {
        return predicate.test(s);
    }

    public static void main(String[] args) {
        String s = "apple";
        boolean b = method(s, (String str) -> {
            return str.length() > 5;
        });
        System.out.println(b);
    }
}
  • 默认方法and(&&)
import java.util.function.Predicate;

public class Main02 {
    public static boolean method(String s, Predicate pr1, Predicate pr2) {
        return pr1.test(s) && pr2.test(s);
    }

    public static void main(String[] args) {
        String s = "apple";
        boolean b = method(s, (String str) -> {
            return s.length() > 4;
        }, (String str) -> {
            return s.contains("a");
        });
        System.out.println(b);
    }
}
  • 默认方法or(||)
  • 默认方法negate( !)
4.Function接口

Java.util.function.Function接口用来根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称之为后置条件。

  • 抽象方法:apply

R apply(T t)根据类型T的参数获取类型的结果

import java.util.function.Function;

public class Main03 {
    public static void change(String s, Function function) {
        Integer in = function.apply(s);//apply为抽象方法
        System.out.println(in);
    }

    public static void main(String[] args) {
        String s = "10";
        change(s, (String str) -> {
            return Integer.parseInt(str);
        });
    }
}
  • 默认方法andThen
import java.util.function.Function;

public class Main03 {
    public static void change(String s, Function fun1, Function fun2) {
        String ss = fun1.andThen(fun2).apply(s);
        System.out.println(ss);
    }

    public static void main(String[] args) {
        String s = "123";
        change(s, (String str) -> {
            return Integer.parseInt(str) + 10;
        }, (Integer i) -> {
            return i + "";
        });
    }
}

9.3 Stream流

"Stream流"其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身不存储任何元素或地址值。数据源,流的来源可以是集合数组等

1.Stream关注的是对数据的运算,与CPU打交道

集合关注的是数据的存储,与内存打交道

2.Stream自己不会存储元素,不会改变源对象。会返回一个持有结果的新Stream

Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行

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

public class Main04 {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("张无忌");
        list.add("赵丽颖");
        list.add("张三丰");
        list.add("刘德华");
        list.add("张强");
        //对集合进行过滤,传统的方法得用循环遍历集合
        list.stream().filter((String name) -> {
            return name.length() == 3;
        }).filter((String name) -> {
            return name.startsWith("张");
        }).forEach((String name) -> {
            System.out.println(name);
        });

        //优化
        list.stream()
                .filter((name) -> name.length() == 3)
                .filter((name) -> name.startsWith("张"))
                .forEach((name) -> System.out.println(name));
    }
}
  • 两种获取Stream流的方法

1.所有的Collection集合都可以用Stream默认方法获取流

List list=new ArrayList<>();Stream=list.stream();

2.Stream接口的静态方法of可以获取数组对应的流

static < T >Stream< T >of (T...values)

参数是一个可变参数,可以传递一个数组

9.3.1 Stream流常用方法

1.延迟方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用

2.终结方法:返回值类型不再是Stream接口自身类型的方法,终结方法包括count和forEach方法

终结方法:forEach:void forEach(Consumeraction);

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        Stream stream = Stream.of("张三", "李四", "田五");
        stream.forEach(name -> System.out.println(name));
    }
}

过滤方法:filter:Streamfilter(Predicatepredicate)

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张三丰", "张翠山", "赵明", "张强");
        stream.filter((name) -> name.startsWith("张"))
                .filter((name) -> name.length() == 3)
                .forEach((name) -> System.out.println(name));
    }
}

Stream流只能被消费一次

计数方法:long count ();这是一个终结方法,用来统计集合中元素的个数

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        List list = new ArrayList<>();
        Collections.addAll(list, 1, 2, 3, 4, 5, 6);
        Stream stream = list.stream();
        long count = stream.count();
        System.out.println(count);
    }
}

截取流中的前n个元素Stream< T >limit(long maxSize)这是一个延迟方法

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "懒洋洋", "沸羊羊"};
        Stream stream = Stream.of(arr);
        Stream stream1 = stream.limit(3);
        stream1.forEach((name) -> System.out.println(name));
    }
}

跳过流中的前n个元素Streamskip(long n)

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "懒洋洋", "沸羊羊","暖洋洋"};
        Stream<String> stream = Stream.of(arr);
        Stream<String> stream1 = stream.skip(3);
        stream1.forEach((name) -> System.out.println(name));
    }
}

把两个流组合为一个流static Streeam concat(Streama,Streamb)

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "懒洋洋", "沸羊羊","暖洋洋"};
        Stream<String> stream = Stream.of(arr);
        Stream<String> stream1 = Stream.of("张三丰", "张翠山", "赵明", "张强");
        Stream<String> stream2=Stream.concat(stream,stream1);
        stream2.forEach((name)-> System.out.println(name));

    }
}

把流中的元素映射到另一个流中 Stream map(Functionmapper);

import java.util.stream.Stream;

public class Main05 {
    public static void main(String[] args) {
        Stream stream = Stream.of("1", "2", "3", "4");
        Stream stream1 = stream.map((String str) -> {
            return Integer.parseInt(str);
        });
        stream1.forEach((Integer i) -> System.out.println(i));
    }
}

9.3.3 方法引用

方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。

  • 使用情境:当要传递给Lambda体的操作,已经有实现方法了,可以使用方法引用

  • 方法引用也是函数式接口的实例

  • 使用格式:类(或对象)::方法名

  • 方法引用的使用要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同

  • 具体分为如下三种情况:

    对象::非静态方法

    类::静态方法

    类::非静态方法

import org.junit.Test;

import java.util.function.Consumer;
import java.util.function.Function;

class MethodTest {
   @Test
    public void Test1() {
        //Lambda表达式
        Consumer con1 = str -> System.out.println(str);
        con1.accept("北京");
        //方法引用(对象::方法)
        Consumer con2 = System.out::println;
        con1.accept("beijing");
    }

    @Test
    public void Test2() {
        Function fun1 = d -> Math.round(d);
        System.out.println(fun1.apply(12.3));

        Function fun2 = Math::round;
        System.out.println(fun1.apply(12.6));
    }
}

9.4 junit单元测试

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class Main06 {
    public int  Add(int a,int b) {
        return a +b;
    }
    @Before
    public void before(){
        System.out.println("所有测试方法之前都会执行该方法");
    }
    @Test
    public void testAdd(){
        int result=Add(3,5);
        //断言,我断言这个结果是8,断言失败的话就会抛出异常
        Assert.assertEquals(8,result);//(期望的结果,运算的结果)
        System.out.println(result);
    }
    @After
    public void after(){
        System.out.println("所有测试方法之后都会执行该方法");
    }
}

9.5 反射

反射:将类的各个部分封装为其他对象,这就是反射机制。

​ 好处:1.可以在程序运行过程中,操作这些对象。2.可以解耦,提高程序的可拓展性。

  • 获取Class对象的方式:

    1.Class.forName(“全类名”):将字节码对象加载进内存,返回Class对象(多用于配置文件,将类名定义在配置文件中。读取文件,加载类))

    2.类名.class:通过类名的属性Class获取(多用于参数的传递)

    3.对象.getClass():getClass()方法在Object类中定义着(多用于对象的获取字节码的方式)

  • 结论: 同一个字节码文件(*.Class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

public class Main {

public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
		//方式一
		Class clazz=Persion.class;
		System.out.println(clazz);//class com.atguigu.contact.Persion
		//方式二
		Persion p =new Persion();
		Class clazz1=p.getClass();
		System.out.println(clazz1);//class com.atguigu.contact.Persion
		//方式三
		Class clazz2=Class.forName("com.atguigu.contact.Persion");
		System.out.println(clazz2);//class com.atguigu.contact.Persion
		//方式四
		ClassLoader classLoader=Main.class.getClassLoader();
		Class clazz3=classLoader.loadClass("com.atguigu.contact.Persion");
		System.out.println(clazz3);//class com.atguigu.contact.Persion
	}

}
Class对象功能:
获取功能:1.获取成员变量们
           Field getField(String name)//返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
           Field[] getFields()//返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。
           Field getDeclaredField(String name)//返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
           Field[] getDeclaredFields()//返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
         2.获取构造方法们
         Constructor getConstructor(类... parameterTypes)//返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。
         Constructor[] getConstructors()//返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。
         Constructor getDeclaredConstructor(类... parameterTypes)//返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。
         Constructor[] getDeclaredConstructors()//返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。
         3.获取成员方法们
         Method getMethod(String name)//返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
         Method[] getMethods()//返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
         Method getDeclaredMethod(String name) //返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。
         Method[] getDeclaredMethods()//返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
         4.获取类名
         String getName()//返回由 类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String 。

9.5.1 获取成员变量们

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Field[] fields = personClass.getFields();//只能获取由public修饰的成员变量
        for (Field field : fields) {
            System.out.println(field);
        }

        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);//能获取所有的成员变量
        }

        Field d = personClass.getDeclaredField("d");//加上Declared能获取所有的成员变量,不考虑修饰符
        d.setAccessible(true);//暴力反射,无法直接访问其他类的私有成员变量,加上暴力反射可以直接访问,忽略访问修饰符的安全检查

        Person p = new Person();
        Object value = d.get(p);
        System.out.println(value);

    }
}

9.5.2 获取构造方法

import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Constructor constructor=personClass.getConstructor(String.class,int.class);
        System.out.println(constructor);//public 反射.Person(java.lang.String,int)
        Object person = constructor.newInstance("小明", 18);
        System.out.println(person);//Person{name='小明', age=18}
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

9.5.3 获取成员方法们

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Person p = new Person();
        Method eat_Method1 = personClass.getMethod("eat");
        eat_Method1.invoke(p);
        
        Method eat_Method2 = personClass.getMethod("eat", String.class);
        eat_Method2.invoke(p, "饭");
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void eat(){
        System.out.println("吃");
    }
    public void eat(String food){
        System.out.println("吃"+food);
    }
}

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Person p = new Person();
        Method eat_Method1 = personClass.getMethod("eat");
        eat_Method1.invoke(p);

        Method eat_Method2 = personClass.getMethod("eat", String.class);
        eat_Method2.invoke(p, "饭");
        //获取所有public修饰的方法(包括Object类的方法)
        Method[] method = personClass.getMethods();
        for (Method method1 : method) {
            System.out.println(method1);
            //获取方法名
            String name = method1.getName();
            System.out.println(name);
        }
        //获取类名
        String name = personClass.getName();
        System.out.println(name);
    }
}
  • 加载配置文件

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BVpFC5Dc-1587174732290)(C:\Users\asus\Desktop\Java.assets\image-20200220192913756.png)]

9.6 Optional类

常用方法

Optional.of(T t);//创建一个Optional实例,t必须非空
Optional.empty();//创建一个空的Optional实例
Optional.ofNullable();//t可以为空
T orElse(T other) //如果存在该值,返回值, 否则返回 other。
T get() //如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException    
void ifPresent(Consumer consumer)//如果值存在则使用该值调用 consumer , 否则不做任何事情。    
boolean isPresent()//如果值存在则方法会返回true,否则返回 false。   
public Stream stream()//stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。    
    

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

import java.util.Optional;

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

        Java8Tester java8Tester = new Java8Tester();
        Integer value1 = null;
        Integer value2 = new Integer(10);

        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> a = Optional.ofNullable(value1);

        // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
        Optional<Integer> b = Optional.of(value2);
        System.out.println(java8Tester.sum(a,b));
    }

    public Integer sum(Optional<Integer> a, Optional<Integer> b){

        // Optional.isPresent - 判断值是否存在

        System.out.println("第一个参数值存在: " + a.isPresent());
        System.out.println("第二个参数值存在: " + b.isPresent());

        // Optional.orElse - 如果值存在,返回它,否则返回默认值
        Integer value1 = a.orElse(new Integer(0));

        //Optional.get - 获取值,值需要存在
        Integer value2 = b.get();
        return value1 + value2;
    }
}

9.7 JDK9&10&11新特性

1. 模块系统

Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,

2.jshell(交互式的编程环境)

它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。

输入/help 可以查看 JShell相关的命令
输入/imports 命令用于查看已导入的包    
输入 /exit 命令退出 jshell    

3. Java 9 私有接口方法

Java9可以在接口中定义私有方法,该方法只能在接口中调用

Object value = d.get§;
System.out.println(value);

}

}


### 9.5.2 获取构造方法



```Java
import java.lang.reflect.Constructor;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Constructor constructor=personClass.getConstructor(String.class,int.class);
        System.out.println(constructor);//public 反射.Person(java.lang.String,int)
        Object person = constructor.newInstance("小明", 18);
        System.out.println(person);//Person{name='小明', age=18}
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

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

9.5.3 获取成员方法们

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Person p = new Person();
        Method eat_Method1 = personClass.getMethod("eat");
        eat_Method1.invoke(p);
        
        Method eat_Method2 = personClass.getMethod("eat", String.class);
        eat_Method2.invoke(p, "饭");
    }
}
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public void eat(){
        System.out.println("吃");
    }
    public void eat(String food){
        System.out.println("吃"+food);
    }
}

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Class personClass = Person.class;

        Person p = new Person();
        Method eat_Method1 = personClass.getMethod("eat");
        eat_Method1.invoke(p);

        Method eat_Method2 = personClass.getMethod("eat", String.class);
        eat_Method2.invoke(p, "饭");
        //获取所有public修饰的方法(包括Object类的方法)
        Method[] method = personClass.getMethods();
        for (Method method1 : method) {
            System.out.println(method1);
            //获取方法名
            String name = method1.getName();
            System.out.println(name);
        }
        //获取类名
        String name = personClass.getName();
        System.out.println(name);
    }
}
  • 加载配置文件

    [外链图片转存中…(img-BVpFC5Dc-1587174732290)]

9.6 Optional类

常用方法

Optional.of(T t);//创建一个Optional实例,t必须非空
Optional.empty();//创建一个空的Optional实例
Optional.ofNullable();//t可以为空
T orElse(T other) //如果存在该值,返回值, 否则返回 other。
T get() //如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException    
void ifPresent(Consumer consumer)//如果值存在则使用该值调用 consumer , 否则不做任何事情。    
boolean isPresent()//如果值存在则方法会返回true,否则返回 false。   
public Stream stream()//stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream,否则返回一个空的 Stream(Stream.empty())。    
    

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 类的引入很好的解决空指针异常。

import java.util.Optional;

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

        Java8Tester java8Tester = new Java8Tester();
        Integer value1 = null;
        Integer value2 = new Integer(10);

        // Optional.ofNullable - 允许传递为 null 参数
        Optional<Integer> a = Optional.ofNullable(value1);

        // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
        Optional<Integer> b = Optional.of(value2);
        System.out.println(java8Tester.sum(a,b));
    }

    public Integer sum(Optional<Integer> a, Optional<Integer> b){

        // Optional.isPresent - 判断值是否存在

        System.out.println("第一个参数值存在: " + a.isPresent());
        System.out.println("第二个参数值存在: " + b.isPresent());

        // Optional.orElse - 如果值存在,返回它,否则返回默认值
        Integer value1 = a.orElse(new Integer(0));

        //Optional.get - 获取值,值需要存在
        Integer value2 = b.get();
        return value1 + value2;
    }
}

9.7 JDK9&10&11新特性

1. 模块系统

Java 9 模块的重要特征是在其工件(artifact)的根目录中包含了一个描述模块的 module-info.class 文 件。 工件的格式可以是传统的 JAR 文件或是 Java 9 新增的 JMOD 文件。这个文件由根目录中的源代码文件 module-info.java 编译而来。该模块声明文件可以描述模块的不同特征。在 module-info.java 文件中,我们可以用新的关键词module来声明一个模块,

2.jshell(交互式的编程环境)

它允许你无需使用类或者方法包装来执行 Java 语句。它与 Python 的解释器类似,可以直接 输入表达式并查看其执行结果。

输入/help 可以查看 JShell相关的命令
输入/imports 命令用于查看已导入的包    
输入 /exit 命令退出 jshell    

3. Java 9 私有接口方法

Java9可以在接口中定义私有方法,该方法只能在接口中调用

你可能感兴趣的:(java)