java基础知识点

(涉及到图片的资源因为在电脑本地,挨个挨个找太浪费时间就不找了)

基础点

**字节:**每逢8位是一个字节,这是数据存储的最小单位。

计算机中的数据转换:

​ 1 Byte = 8 bit

​ 1 KB = 1024 Byte

​ 1 MB = 1024 KB

​ 1 GB = 1024 MB

​ 1 TB = 1024 GB

​ 1 PB = 1024 TB

​ 1 EB = 1024 PB

​ 1 ZB = 1024 EB

命令符基本指令(cmd):
  • MS-DOS(Microsoft Disk Operationg System)磁盘操作系统

  • 磁盘切换[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrQB31CZ-1636071520894)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210102202035418.png)]

  • 进入磁盘下面的文件夹:cd + 文件夹的名称

  • 返回该文件夹上一级:cd + … {cd + \}

  • 显示当前文件夹下面的文件:dir

  • 清空DOS界面: cls

  • 退出: exit

JVM(java 虚拟机):

​ 是运行所有java程序的假象计算机,是java的运行环境,具有跨平台性

JRE(Java Runtime Environment):

​ 是java程序运行时环境,包含JVM和运行时所需要的核心类库

JDK(Java Development Kit):

​ 是java程序开发工具包,包含JRE和开发人员使用的工具

命名规则(软性要求):

  • 类名规范:首字母大写,后面的每个单词的首字母都大写(大驼峰式)
  • 变量名和方法名规范:首字母小写,后面的每个单词的首字母都大写(小驼峰式)
基本数据类型:
  • 整数型:byte short int long
  • 浮点型:float double
  • 字符型:char
  • 布尔型:boolean
数据类型 关键字 内存占用 取值范围
字节型 byte 1个字节 -128~127
短整型 short 2个字节 -32768~32767
整型 int(默认) 4个字节 -231~231-1
长整型 long 8个字节 -263~263-1
单精度浮点数 float 4个字节 1.4E-45 ~3.4028E+38
双精度浮点数 double(默认) 8个字节 4.9E-324~1.797E+308
字符型 char 2个字节 0-65535
布尔类型 boolean 1个字节 true false

注意定义long类型的数值的时候需要在后面加一个L,float类型的数值赋值也需要在后面加一个F,不然默认的是double类型

//例如
long num = 30000000L;
float num1 = 2.5F;
强制类型转换
  • 格式:范围小的类型 范围小的变量名 = (范围小的类型)原本范围大的数据;

  • 注意事项:

    • 强制类型转换类型一般不推荐使用,因为有可能发生精度损失、数据溢出。

    • byte/short/char这三种类型都可以发生数学运算,例如加法“+”。

    • byte/short/char这三种类型在运算的时候,都会被首先提升成为int类型,然后再计算。

      //比如byte+byte
      byte num1 = 40;
      byte num2 = 50;
      byte result = num1 + num2;//此时这一行会报错,因为byte和byte的相加会提升为int和int相加,最后返回的一个结果就是int,正确的写法如下
      int result = num1 + num2;//当然也可以用byte将结果强制类型转换
      
    • boolean类型不能发生数据类型转换

数字和字符的对照关系表(编码表):
  • ASCII码表:American Standard Code for Information Interchange(美国信息交换标准代码)。
  • Unicode码表:万国码,也是数字和字符的对照关系,开头0-127部分和ASCII完全一样,但是从128开始包含有更多字符。

运算:

  • 四则运算

    • 注意:一旦运算当中有不同类型的数据,那么结果将会是数据类型范围大的那种。
JShell的简单实用:

​ JShell是JDK9新出的轻量级编译,相当于脚本一句一句进行编译,使用方法也非常简单,直接在dos窗口中输入jshell,就可以进入,注意退出的时候使用【/exit】进行退出,这是固定的退出格式。

编译器的两点优化:
  • 常量优化

    /*
    在给变量进行赋值的时候,如果右侧的表达式当中全部都是常量,没有任何变量,
    那么编译器javac将会直接将若干个常量表达式计算得到结果。
    short result = 5+8; //等号右边全都是常量,没有任何变量参与运算
    编译之后,得到的.class字节码文件当中相当于【直接就是】:short result = 13;这称为“编译器的常量优化”但是注意:一旦表达式当中有变量参与,那么就不能进行这种优化了。
    */
    Public class DemoNotice{public static void main(String[] args){short a = 5;short b = 8;short result = a + b;  //错误的写法,这是不被允许的,a和b进行运算的时候会自动提升为int和int进行计算,导致左右两边的类型不匹配short result = 5 + 8;}
    }
    

switch语句特点:

注意事项:

  • 多个case后面的数值不可以重复
  • switch后面小括号当中只能是下列数据类型:
    • 基本数据类型:byte/short/char/int
    • 引用数据类型:String字符串/enum枚举
  • switch语句格式可以很灵活:前后顺序可以颠倒,而且break语句还可以省略。“匹配哪一个case就从哪一个位置向下执行,直到遇到了break或者整体结束为止。”

死循环的标准格式

//死循环的标准格式
while(true){循环语句;
}//随便写一条语句
System.out.println("hello world!");   //此时整个程序都会报错,表示识别不到这个语句,因为前面那个是死循环,所以程序根本不能运行到这一句,所以识别不到
IDEA快捷键
Ctrl+Alt+L    //格式化代码
Alt+Enter     //导入包
Ctrl+shift+↑↓ //将当前代码行上下移动
Alt+Insert    //自动生成toString、get、set等方法

定义方法的注意事项:

  • 方法的定义应该在类当中,不应该在方法当中再定义方法
  • 方法定义的前后顺序无所谓
  • 方法有单独调用,打印调用,赋值调用

**方法重载(OverLoad):**方法名相同,但是参数列表不同(参数个数,参数类型,参数类型顺序)。

数组:
  • 动态初始化(长度):在创建数组的时候,直接指定数组当中的数据元素个数

    //固定格式:数据类型[] 数组名称 = new 数据类型[数组长度];
    int[] arrayA = new int[300];
    double[] arrayB = new double[300];
    String[] arrayC = new String[300];
    /*
    1、动态初始化也可以拆分成为两个步骤。
    */
    
  • 静态初始化:在创建数组的时候,不直接指定数据个数多少,而是直接将具体的数据内容进行指定

    //基本格式:数据类型[] 数组名称 = new 数据类型[]{元素1,元素2,.....}
    int[] arrayA = new int[]{1, 2, 3, 4, 5};
    String[] arrayB = new String[]{"Hello", "World", "Java"};
    //省略格式:数据类型[] 数组名称 = {元素1,元素2,......};
    int[] arrayA = {1, 2, 3, 4, 5};
    /*
    注意事项:
    1、静态初始化没有直接指定长度,但是仍然会自动推算得到长度。
    2、静态初始化标准格式剋拆分成为两个步骤。
    3、静态初始化一旦使用省略格式,就不能拆分成为两个步骤了。
    */
    
  • 数组的遍历

    //不能直接打印数组名称,这样输出的结果是数组对应的内存地址哈希值
    //访问数组元素的时候,可以通过数组+索引进行访问:数组名[索引值]
    /*
    注意事项:
    静态数组初始化其实也有默认值的过程,只不过系统自动马上将默认值替换成为了大括号当中的具体数值。
    */
    
  • 数组作为方法参数

    //数组作为方法参数传入的时候
    public static void printArray(int[] array){for(int i = 0; i < array,length; i++)System.out.println(array[i]);
    }
    
  • 数组作为返回值

    //数组作为返回值时
    class demo{public static void main(String[] args){int[] result = calulate(int 10, int 20, int 30);}public staic int[] calulate(int a, int b, int c){int sum = a + b + c;int avg = sum/3;int[] array = new int[2];array[0] = sum;array[1] = avg;return array;}
    }
    
Java的内存划分:

Java的内存需要划分成为5个部分:

​ 1、栈(Stack):存放的都是方法中的局部变量。方法的运行一定要在栈当中。

​ 局部变量:方法的参数,或者是方法体内部的变量

​ 作用域:一旦超出作用域,立刻从栈内存 当中消失

​ 2、堆(Heap):凡是new出来的东西,都放在堆中。

​ 堆内存里面的东西都有一个地址值:16进制

​ 堆内存里面的数据,都有默认值。规则:

如果是整数 默认为0
如果是浮点数 默认为0.0
如果是字符 默认为’\u0000’
如果是布尔 默认为false
如果是引用类型 默认为null

​ 3、方法区(Method Area):存储.class相关信息,包含方法的信息。

​ 4、本地方法栈(Native Method Stack):与操作系统相关。

​ 5、寄存器(pc Register):与CPU相关。

一个数组内存图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDDaa0Bi-1636071520898)(D:\photo\一位数组内存图.jpg)]

二个数组内存图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jx6Mojgq-1636071520900)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210115163040808.png)]

有关数组的函数:

//获取数组的长度
int[] arr = new int[]{1,2,3,4,5,6,7,8,9,10};
int len = arr.length;   //这样就可以直接获取数组的长度

面向对象:

/*
面向对象的思想:可以不考虑细节的过程,而直接看整体
*/

类与对象

**类的定义:**是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。

  • 属性:就是该事物的状态信息。
  • 行为就是该事物能够做什么。

**对象的定义:**是一类事物的具体体现。对象时类的一个实例,必然具备该类事物的属性和行为。

类是对象的模板,对象时类的实例。

类中的成员变量默认的情况下是protected。

调用对象的内存图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAU4vsiS-1636071520902)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210116100748053.png)]

两个对象适用一个方法的内存图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AvHzgSu9-1636071520904)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210116101613457.png)]

当一个对象作为参数,传递到方法当中时,实际上传递进去的是对象的地址值,同理当一个对象作为返回值返回时,返回的也是对象的地址值

局部变量和成员变量:

  • 定义的位置不一样

    • 局部变量:在方法的内部
    • 成员变量:在方法的外部,直接写的类当中
  • 作用范围不一样
    • 局部变量:只有方法当中才可以使用,除了方法就不能再用
    • 成员变量:整个类全都可以通用
  • 默认值不一样
    • 局部变量:没有默认值,如果要想使用,必须手动进行赋值
    • 成员变量:如果没有赋值,会有默认值,规则和数组一样
  • 内存的位置不一样
    • 局部变量:位于栈内存
    • 成员变量:位于堆内存
  • 生命周期不一样
    • 局部变量:随着方法进栈而诞生,随着方法出栈而消失
    • 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失

this关键字

​ 为了区别于成员变量同名的参数,通过谁调用的方法,谁就是this

标准的类(Java Bean):

一个标准的类通常要拥有下面四个组成部分:

  • 所有的成员变量都要使用private关键字修饰
  • 为每一个成员变量编写一对儿Getter/Setter方法
  • 编写一个无参数的构造方法
  • 编写一个全参数的构造方法

这样标准的类也叫做Java Bean。

API

概述:API(Application Programming Interface),应用程序编程接口。Java API是一本程序员的字典,是JDK中提供给我们使用的类的说明文档。这些类降低成的代码实现封装了起来,我们不需要关心这些类是如果实现的,只需要学习这些类如何使用即可。所以我们可以通过查询API的方式,来学习Java提供的类,并得知如何使用它们。

API使用步骤
  • 打开帮助文档。
  • 点击显示,找到索引,看到输入框。
  • 要找谁?在输入框里输入,然后回车。
  • 看包。java.lang下的类不需要导包,其他需要。
  • 看类的解释和说明。
  • 学习构造方法。
  • 使用成员方法。

注意:只用java.lang下面的内容不需要导入包,其他的都需要导入包。

Scanner类的使用

//基本使用格式
Scanner sc = new Scanner(System.in);
//这里的System.in在我们学习的前期基本是固定格式,代表从键盘输入的意思
/*
使用方法:
对象名.成员方法名()
*/
//获取键盘输入一个int数字:
int num = sc.nextInt();
//获取键盘输入的一个字符串:
String str = sc.next();
匿名对象

​ 匿名对象就是只有右边的对象,没有左边的名字和赋值运算符。

/*
匿名对象的格式
new 类名称();
*/
new Person().name;
//注意事项:匿名对象只能使用唯一的一次,下次再用不得不再创建一个新对象。
//使用建议:如果确定有一个对象只需要使用唯一的一次,就可以用匿名对象。//匿名对象作为方法的参数和返回值
//普通使用方式
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();//匿名对象的方式
int num = new Scanner(System.in);//使用一般写法传入参数
Scanner sc = new Scanner(System.in);
methodParam(sc);
//使用匿名对象来进行传参
methodParam(new Scanner(System.in));public static void methodParam(Scanner sc){int num = sc.nextInt();System.out.println("输入的是:" + num);
}//Scanner作为返回值
Scanner sc = methodReturn();
int num = sc.nextInt();
System.out.println("输入的是:" + num);public static Scanner methodReturn(){//一般写法Scanner sc = new Scanner(System.in);return sc;//匿名对象写法return new Scanner(System.in);
}
Random类
//用法也和Scanner类相似
Random r = new Random();
int num = r.nextInt();
int count = r.nextInt(10);  //取到0-9的随机数
ArrayList类
//数组的长度不可以发生改变,但是ArrayList集合的长度是可以随意变化的
/*
对于ArrayList来说,有一个尖括号<E>代表泛型
泛型:也就是装在集合当中的所有元素,全都是统一的什么类型
注意:泛型只能是引用类型,不能是基本类型
*/
ArrayList<String> list = new ArrayList<>();//此时就创建了一个字符串数组/*
注意事项:对于ArrayList集合来说,直接打印得到的不是地址值,而是内容如果内容是空,得到的是空的中括号:[]向集合中添加一些数据,需要用到add方法
*//*
如果希望向集合ArrayList当中存储基本类型数据,必须使用基本类型对应的“包装类”基本类型  包装类(引用类型,包装类都位于java.lang包下)
byte        Byte
short       Short
int         Integer
long        Long
float       Float
double      Double
char        Character
boolean     Boolean从JDK1.5开始,支持自动装箱、自动拆箱
自动装箱:基本类型 --> 包装类型
自动拆箱:包装类型 --> 基本类型
*/

ArrayList当中常用的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZgj0AMw-1636071520906)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210119175146257.png)]

字符串的概述和特点

概述:java.lang.String类代表字符串。

API当中说:java程序中的所有字符串字面值(如“abc”)都作为此类的实例实现。

其实就是说:程序当中所有的双引号字符串,都是String类的对象。(就算没有new,也照样是。)

特点

  • 字符串的内容永不可变。
  • 正是因为字符串不可改变,所以字符串是可以共享使用的。
  • 字符串效果上相当于是char[]字符数组,但是底层原理是byte[]字节数组。

创建字符串的常见3种构造方法

  • public String():创建一个空白字符串,不含有任何内容。

  • public String(char[] array):根据字符串数组的内容,来创建对应的字符串。

  • public String(byte[] array):根据字节数组的内容,来创建对应的字符串。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AznHw7y8-1636071520907)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210121121225089.png)]

字符串常量池

/*
字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。对于基本类型来说, ==是进行数值的比较。
对于引用类型来说, ==是进行地址值的比较。
*/public static void main(String[] args){String str1 = "abc";String str2 = "abc";char[] charArray = {'a', 'b', 'c'};String str3 = new String(charArray);System.out.println(str1 == str2); //trueSystem.out.println(str1 == str3); //falseSystem.out.println(str2 == str3); //false
}

字符串常量池[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FLLrcHjt-1636071520908)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210121200038939.png)]

字符串中与比较常用的方法

/*
public boolean equals(Object obj):参数可以是任何对象,只有参数是一个字符串并且内容相同的才会给true;否则返回false。
注意事项:
1、任何对象都能用Object进行接受。
2、equals方法具有对称性,也就是a.equals(b)和b.equals(a)效果一样。
3、如果比较双方一个常量一个变量,推荐把常量字符串写在前面。
推荐:"abc".equals(str)       不推荐:str.equals("abc")
*/public static void main(String[] args){String str1 = "Hello";String str2 = "Hello";char[] charArray = {'H', 'e', 'l', 'l', 'o'};String str3 = new String(charArray);System.out.println(str1.equals(str2));      //trueSystem.out.println(str2.equals(str3));        //trueSystem.out.println(str3.equals("Hello"));   //trueSystem.out.println("Hello".equals(str1));   //true
}/*
public boolean equalsIgnoreCase(String str):忽略大小写,进行内容比较
*/
public static void main(String[] args){String str = "abc";System.out.println("Abc".equalsIgnoreCase(str));
}

字符串中与获取相关的常用方法

/*
String当中与获取相关的常用方法有:
public int length():获取字符串当中含有的字符个数,拿到字符串长度。
public String concat(String str):将当前字符串和参数字符串拼接成为返回值新的字符串。
public char charAt(int index):获取指定索引位置的单个字符。(索引从0开始)
public int indexOf(String str):查找参数字符串在本字符串当中首次出现的索引位置,如果没有返回-1值。
*/

字符串中与截取相关的常用方法

/*
字符串的截取方法:public String substring(int index):截取从参数位置一直到字符串末尾,返回新字符串。
public String substring(int begin,int end):截取从begin开始,一直到end结束,中间的字符串。
备注:[begin,end),包含左边,不包含右边。
*/public static void main(String[] args){String str1 = "HelloWorld";String str2 = str1.substring(5);System.out.println(str2);   //WorldString str3 = str1.substring(4,7);  //oWo
}

字符串与转换相关的常用方法

/*
String当中与转换相关的常用方法:public char[] toCharArray():将当前字符串拆分成为字符数组作为返回值。
public byte[] getBytes():获得当前字符串的字节数组。
public String replace(CharSequence oldString, CharSquence newString):将所有出现的老字符串替换成为新的字符串,返回替换之后的结果新字符串
备注:CharSequence意思就是说可以接受字符串类型。
*/

字符串与分割相关的常用方法

/*
分割字符串的方法:public String[] split(String regex):按照参数的规则,将字符串切分成为若干部分。注意事项:
split方法的参数其实是一个”正则表法式“,要注意,如果按照英文句点”.“进行切分,必须写”\\."(两个反斜杠)
*/

Static关键字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DHjtRKM1-1636071520909)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210123135134290.png)]

含义:一旦用了static关键字,那么这也难怪的内容不再属于对象自己,而是属于类的,所有凡是本类的对象,都共享同一份。

/*
static修饰成员变量的时候,一旦对象给该成员变量赋值,所有对象都会共享这个值。
*/
class Student{private int id;private String name;private int age;private static int idCounter = 0;static String room;public Student(){this.id = ++idCounter;}public Student(String name, int age){this.name = name;this.age = age;this.id = ++idCounter;}//成员变量的get和set方法...
}class Test{public static void main(String[] args){Student one = new Student("郭靖","19");one.room = "内江六中";System.out.println(one.getName() + " " + one.getAge() + " " + one.getRoom() + "  " + one.getId());Student two = new Studnet("黄蓉", "20");System.out.println(two.getName() + " " + two.getAge() + " " + two.getRoom() + "  " + two.getId());   //此时的two的room依然会显示内江六中,因为数据是共享的}
}

用static修饰成员方法

/*
一旦用static修饰成员方法,那么这就成为了静态方法。静态方法不属于对象,而是属于类的。如果没有static关键字,那么必须首先创建对象,然后通过对象才能使用它。
如果有了static关键字,那么不需要创建对象,直接就能通过类名称进行调用。
并且静态方法推荐使用类名称进行直接调用无论是成员变量,还是成员方法。如果有了static,都推荐使用类名称进行调用。
静态变量:类名称.静态变量
静态方法:类名称.静态方法()注意事项:
1、静态不能直接访问非静态。
原因:因为在内存当中是【先】有的静态内容,【后】有的非静态内容。
“先人不知道后人,但是后人知道先人。“
2、静态方法当中不能用this。
原因:this代表当前对象,通过谁调用的方法,谁就是当前对象。
*/

静态static内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0E7HdfI-1636071520910)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210123155550259.png)]

静态代码块

/*
静态代码块的格式:public class 类名称{static{//静态代码块的内容}
}特点:当第一次用到本类时,静态代码块执行唯一的一次。静态代码块的典型用途:
用来一次性的堆静态成员变量进行赋值。
*/public class Demo04Static{public static void main(String[] args){Person one = new Person();Person two = new Person();//此时静态代码块只会运行一次,不会因为对象的再次创建而再次执行}
}public class Person{static {System.out.println("静态代码块的执行");}public Person(){System.out.println("构造方法的执行");}
}
工具类
  • 数组工具类Arrays

    /*
    java.util.Arrays是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操作。public static String toString(数组):将参数数组变成字符串(按照默认的格式[元素1,元素2,...])
    public static void sort(数组):按照默认升序(从小到大)对数组元素进行排序。注意事项:
    1、如果是数值,sort默认按照升序从小到大
    2、如果是字符串,sort默认按照字母升序
    3、如果是自定义的类型,那么这个自定义的类需要有Comparable或者Comparator接口的支持。(今后学习)
    */public static void main(String[] args){int[] intArray = {10, 20, 30};String intstr = Arrays.toString(intArray);System.out.println(intstr);int[] array1 = {2, 1, 3, 10, 6};Arrays.sort(array1);System.out.println(Arrays.toString(array1));
    }
    
  • 数学工具类Math

    /*
    java.util.Math类是数学相关的工具类,里面提供了大量的静态方法,完成与数学运算相关的操作。public static double abs(double num):获取绝对值。
    public static double ceil(double num):向上取整
    public static double floor(double num):向下取整
    public static long round(double num):四舍五入
    */
    
面向对象三大特征之一继承性

​ 继承主要解决的问题就是:共性抽取

继承的特点:

  • Java是单继承的
  • Java语言可以多级继承
  • Java中一个子类只能有一个父类,但是一个父类可以有多个子类。

​ 继承是多态的前提

/*
当父类和子类的成员变量重名的时候
并且在子类中还有重名的局部变量子类中访问这些变量的区别:
局部变量:          直接写成员变量名
本类的成员变量: this.成员变量名
父类的成员变量: super.成员变量名
*//*
重写(Override)
概念:在继承关系当中,方法的名称一样,参数列表一样。重写(Override):方法的名称一样,参数列表【也一样】。
重载(Override):方法的名称一样,参数列表【不一样】。注意事项:
1、必须保证父子类之间方法的名称相同,参数列表也相同。
@Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。2、子类方法的返回值必须【小于等于】父类方法的返回值范围。
前提:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。3、子类方法的权限必须【大于等于】父类方法的权限修饰符。
小扩展提示:public > protected > (default) > private
备注:(default)不是关键字,而是什么都不写。(如下)
*/
public int age;
protected int age;
int age;
private int age;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vByUb10y-1636071520912)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210125120207068.png)]

重写父类方法的时候,可以不用重新写父类方法中的重复代码,直接用super关键字在重写方法中调用该父类方法,然后添加新的内容。

继承中构造方法的访问特点
/*
继承关系中,父子类构造方法的访问特点:1、子类构造方法当中有一个默认的“super()"调用,所以一定是先调用的父类构造,后执行的子类构造。2、子类构造可以通过super关键字来调用父类重载构造。3、super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造。
*/public class Fu{public Fu(){System.out.println("父类构造方法执行!");}
}public class Zi extends Fu{public Zi(){//不管这里super()写没写,都会默认给送一个,要是写了就不送,不写就送。//super();     System.out.println("子类构造方法执行!");}
}public class Test{public static void main(String[] args){Zi zi = new Zi(); /*这里的执行结果:父类构造方法执行!子类构造方法执行!*/}
}
/*
super关键字用来访问父类内容,而this关键字用来访问本类内容。用法也有三种:1、在本类的成员方法中,访问本类的成员变量。
2、在本类的成员方法中,访问本类的另一个成员方法。
3、在本类的构造方法中,访问本类的另一个构造方法。在第三种用法当中要注意:
A、this(...)调用也必须是构造方法的第一个语句,唯一一个。
B、super和this两种构造调用,不能同时使用。(尽管不写super(...),父类的构造方法在子类创建对象的时候仍然能够调用)
*/

super和this关键字图解[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZ7KLtTg-1636071520912)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210125124554056.png)]

抽象类

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

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

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

  • 不能直接创建new抽象对象。

  • 必须用一个子类来继承抽象父类。

  • 子类必须覆盖重写抽象父类当中所有的抽象方法。

    覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。

    public abstract class Animal{public abstract void eat();
    }public class cat extends Animal{@Overridepublic void eat(){}
    }
    
  • 创建子类对象进行使用。

一个抽象类不一定含有抽象方法,只要保证抽象方法所在的类是抽象类,即可。

这样没有抽象方法的抽象类,也不能直接创建对象,在一些特殊场景下有用途。

接口

/*
接口就是多个类的公共规范。
接口是一种引用数据类型,最重要的内容就是其中的抽象方法。如何定义一个接口的格式:
public interface 接口名称{//接口内容
}如果是Java 7,那么接口中可以包含的内容有:
1、常量
2、抽象方法如果是Java 8,还可以额外包含有:
3、默认方法
4、静态方法如果是Java 9,还可以额外包含有:
5、私有方法
*/public interface Demo{}
/*
在任何版本的java中,接口都能定义抽象方法。
格式:
public abstract 返回值类型 方法名称(参数列表);注意事项:
1、接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
2、这两个关键字修饰符,可以选择性的省略。
3、方法的三要素,可以随意定义。
*/
public interface Demo{//这是一个抽象方法public abstract void method1();//这也是一个抽象方法abstract void method2();//这也是一个抽象方法public void method3();//这也是一个抽象方法void method4();
}/*
接口使用步骤:
1、接口不能直接使用,必须有一个“实现类”来实现接口。
格式:
public class 实现类名称 implements 接口名称{//....
}2、接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
实现:去掉abstract关键字,加上方法体大括号。3、创建实现类的对象,进行使用。
*/
/*
从Java 8开始,接口里允许定义默认方法。
格式:public default 返回值类型 方法名称(参数列表){//方法体
}备注:接口当中的默认方法,可以解决接口升级的问题。
*/public interface DemoInterfaceDefault {public abstract void method();public default void methodAbs(){System.out.println("make this method run ...");}
}public class DemoInterfaceDefaultImplA implements DemoInterfaceDefault {@Overridepublic void method() {System.out.println("A is running ...");}
}public class DemoInterfaceDefaultImplB implements DemoInterfaceDefault {@Overridepublic void method() {System.out.println("B is running ...");}@Overridepublic void methodAbs(){System.out.println("B中重写默认方法");}
}public class DemoMainTest {public static void main(String[] args) {DemoInterfaceDefaultImplA a = new DemoInterfaceDefaultImplA();a.method();a.methodAbs();System.out.println("==========");DemoInterfaceDefaultImplB b = new DemoInterfaceDefaultImplB();b.method();b.methodAbs();}
}
/*
接口的实现类实现接口,尽管接口的默认方法没有在实现类中写,但是仍然可以进行调用,并且可以在实现类中重写接口中的默认方法。
*/

接口的静态方法使用

/*
从Java 8 开始,接口当中允许定义静态方法。
格式:
public static 返回值类型 方法名称(参数列表){方法体
}注意:不能通过接口实现类的对象来调用接口当中的静态方法。
正确方法:通过接口名称,直接调用其中的静态方法。
格式:
接口名称.静态方法名(参数)
*/
public interface MyInterface {public static void runMethod(){System.out.println("接口静态方法执行");}
}public class MyMain{public static void main(String[] args){MyInterface.runMethod();}
}

接口的私有方法定义

/*问题描述:
我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。
但是这个共有方法不应该让实现类使用,应该是私有化的。解决方案:
从Java 9开始,接口当中允许定义私有方法。
1、普通私有方法,解决多个默认方法之间重复代码问题。
格式:
private 返回值类型 方法名称(参数列表){方法体
}2、静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){方法体
}*/public interface MyInterface {public void method();public default void methodA(){System.out.println("A is running ...");methodC();}public default void methodB(){System.out.println("B is running ...");methodC();}private void methodC(){System.out.println("111");System.out.println("222");System.out.println("333");}
}
/*
此时在该接口的实现类中是不能够对methodC方法进行访问的,只有该接口中的默认方法能够对其进行访问,同样的静态私有方法的抽取也是只能在在接口的静态方法中进行访问,实现类是不能够进行访问的。
*/

接口的常量定义和使用

/*
接口当中也可以定义“成员变量”,但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的【常量】。
格式:
public static final 数据类型 常量名称 = 数据值;注意:
1、在接口中就算定义“成员变量”的时候不加上这三个修饰词,Java也会默认给加上,不写也照样是常量。
2、接口当中的常量,必须进行赋值,不能不赋值。
3、接口中常量的名称,使用完全大写的字母,用下划线进行分割。(推荐的命名规则)
*/
public interface MyInterfaceConst {//这其实就是一个常量,一旦赋值,不可以修改public static final int NUM_OF_MYCLASS = 10;
}
public class MyInterfaceMain {public static void main(String[] args) {System.out.println(MyInterfaceConst.NUM_OF_MYCLASS);}
}
接口内容总结
在Java 9+版本中,接口的内容可以有:(用“[]”括起来的修饰符可以省略)
1、成员变量其实是常量,格式:
[public] [static] [final] 数据类型 常量名称 = 数据值;(也等于==》  数据类型 常量名称 = 数据值)
注意:常量必须进行赋值,而且一旦赋值不能改变。常量名称完全大写,用下划线进行分割。2、接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型 方法名称(参数列表);3、从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型 方法名称(参数列表){  方法体 }4、从Java 8开始,接口里允许定义静态方法,格式:
[public] static 返回值类型 方法名称(参数列表){   方法体 }
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法5、从Java 9开始,接口里允许定义私有方法,格式:
普通私有方法:private 返回值类型 方法名称(参数列表){ 方法体 }
静态私有方法:private static 返回值类型 方法名称(参数列表){  方法体 }
注意:private的方法只有接口自己才可以调用,不能被实现类或别人调用。

继承父类并实现多个接口

使用接口的时候,不要注意:1、接口是没有静态代码块或者构造方法的。
2、一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {//覆盖重写所有抽象方法
}
3、如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
4、如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
5、如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行重写。
6、一个类如果直接父类当中的方法和接口当中的默认方法产生了冲突,优先使用父类当中的方法。
接口之间的多继承
/*
1、类和类之间是单继承的。直接父类只有一个。
2、类与接口之间是多实现的,一个类可以实现多个接口。
3、接口与接口之间是多继承的。注意事项:
1、多个父接口当中的抽象方法如果重复,没关系。
2、多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】。
*/

多态

(继承是多态的前提:extends继承或者implements实现,是多态性的前提)

举例说明:
小明是一个对象,这个对象既有学生形态,也有人类形态。
一个对象拥有多种形态,这就是:
==对象的多态性==
(多态性是形容对象的,不是形容类的)
/*
代码当中体现多态性,其实就是一句话:父类引用指向子类对象格式:
父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称();*/
public class Fu {public void method(){System.out.println("父类方法!");}public void methodFu(){System.out.println("父类特有方法!");}
}public class Zi extends Fu {@Overridepublic void method() {System.out.println("子类方法!");}
}public class DemoMultiMain {public static void main(String[] args) {Fu obj = new Zi();obj.method();obj.methodFu();}
}/*
在多态的代码当中,成员方法的访问规则是:看new的是谁,就优先用谁,没有则向上找。口诀:
1、成员变量:编译看左边,运行还看左边。
2、成员方法:编译看左边,运行看右边。
有关成员方法口诀解释一下:编译的时候看用父类里边有没有该方法,如果没有的话就报错,有的话就正常运行,运行的时候,就看子类是否重写了该方法,要是子类重写了就优先用子类的,要是没有的话,就用父类的。当然如果父类未定义,子类定义了该成员方法,也可以进行调用,需要进行强制转换
格式:((子类) 对象名).成员方法;
*/
多态的好处

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ch3OMLVS-1636071520914)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210201165829452.png)]

对象的向上向下转换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iFQRytMQ-1636071520915)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210201173441440.png)]

/*
转换过程怎么样才能知道一个父类引用的对象,本来是什么子类?
格式:
对象 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。
*/
public static void main(String[] args){Animal animal = new Cat();animal.eat();if(animal instanceof Dog){Dog dog = (Dog) animal;dog.watchHouse();}if(animal instanceof Cat){Cat cat = (Cat) animal;cat.catchMouse();}
}

综合案例(笔记本USB接口)

分析图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M214KQti-1636071520916)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210201174947341.png)]

**项目代码:**Demo06下面的democomputer

Final关键字

final关键字代表最终、不可改变的。

/*
常见四种用法:
1、可以用来修饰一个类当final关键字用来修饰一个类的时候,格式:public final class 类名称 {//...}含义:当前这个类不能有任何的子类。注意:一个类如果是final的,那么其中的所有成员方法都不能被覆盖重写(因为没有子类)。2、可以用来修饰一个方法当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。格式:修饰符 final 返回值类型 方法名称(参数列表){//...}注意事项:对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。(因为用了           abstract修饰的方法必须进行覆盖重写,而final修饰的方法不能被覆盖重写,所以矛盾)3、还可以用来修饰一个局部变量一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。“一次赋值,终生不变”(只要保证有唯一一次赋值就是正确的)注意:1、对于基本类型,不可变说的是变量当中的数据不可变2、对于引用类型来说,不可变说的是变量当中的地址值不可变例如:图片例14、还可以用来修饰一个成员变量对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不可变的。1、由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。2、对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。3、必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
*/

图片例1[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uXNDJ5cB-1636071520917)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210202085850234.png)]

Java中的四种权限修饰符

public > protected > (default) > private

public protected (default) private
同一个类 YES YES YES YES
同一个包 YES YES YES NO
不同包子类 YES YES NO NO
不同包非子类 YES NO NO NO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFi7EBdu-1636071520919)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210202093802242.png)]

内部类:

内部类:
如果一个事物的内部包含另一个事物,那么这就是一个类内包含另一个类。
例如:身体和心脏的关系。又如:汽车和发动机的关系。

分类:

1、成员内部类:
/*
成员内部类如果一个类是定义在一个类里面,那么这么一个类就是局部内部类。修饰符 class 类名称 {修饰符 class 类名称 {//...}//...}注意:内用外,随意访问;外用内,需要内部类对象。
===================================================如何使用成员内部类?有两种方式:1、间接方式:在外部类的方法当中,使用内部类;然后main只是调用外部类的方法。2、直接方式,公式:【外部类名称.内部类名称 对象名 = new 外部类名称().内部类名称();】当成员变量出现了重名现象,想要在内部类调用外部类的成员变量,那么格式是:外部类.this.外部类成员变     量名*///成员变量重名的情况
public class Outer {int num = 10;public class Inner{int num = 20;public void methodInner(){int num = 30;System.out.println(num);System.out.println(this.num);System.out.println(Outer.this.num);}}
}

内部类(inner class)是定义在另一个类中的类,内部类中的方法可以访问创建该内部类的类(outer class)的域中所有数据(包括私有/private数据)。并且,内部类可以对同一个包中的其他类隐藏起来。

2、局部内部类:
/*
局部内部类:如果一个类是定义在一个方法内部的,那么这就是一个局部内部类“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。定义格式:修饰符 class 外部类名称 {修饰符 返回值类型 外部类方法名称(参数列表){class 局部内部类名称 {//...}}}小结一下类的权限修饰符:public > protected > (default) > private定义一个类的时候,权限修饰符规则:1、外部类:public / (default)2、成员内部类:public / protected / (default) /private3、局部内部类:什么都不能写(虽然都是不写,与default其实是不一样)
*/public class Outer{public void methodOuter(){class Inner{int num = 0;public void methodInner(){System.out.println(num);}}Inner inner = new Inner();inner.methodInner();}
}

当某一个类只被唯一一个方法调用时,就可以采用局部内部类来定义它(该类定义在某方法内部)。局部类不能使用public或者private访问说明符进行声明,它的作用域被限定在声明这个局部类的方法(或代码块)中。并且,局部类对外部世界是完全隐藏起来的,及时定义它的方法所在的外围类也不可以访问它。

由于局部内部类对于外围类不可见,所以不存在用过其他手段调用该局部内部类的危险,也就极大的保证了安全性。

局部内部类的final问题

/*
局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效的final的】。
备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略
原因:
1、new出来的对象在堆内存当中。
2、局部变量是跟着方法走的,在栈内存当中。
3、方法运行结束之后,立刻出栈,局部变量就是立刻消失。
4、但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
(生命周期的问题)
*/
匿名内部类定义:

假如一个局部内部类只被用一次(只用它构建一个对象),就可以不用对其命名了,这种没有名字的类被称为匿名内部类(anonymous inner class),其代码格式通常为:

new SuperType( ){

Inner class methods and data //匿名内部类的书写和数据

}

其中SuperType可以是一个接口(匿名内部类将要实现的接口),也可以是一个类(匿名内部类将要扩展它)。匿名内部类的可见域与局部内部类相同。

/*
如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,
那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】匿名内部类的定义格式:
接口名称 对象吗 = new 接口名称() {//覆盖重写所有抽象方法
}对格式进行解析“new 接口名称(){...}进行解析:
1、new代表创建对象的动作
2、接口名称就是匿名内部类需要实现哪个接口
3、{...}这才是匿名内部类的内容另外还要注意几点问题:
1、匿名内部类,在【创建对象】的时候,只能使用唯一一次
如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类了。2、匿名对象,在【调用方法】的时候,只能调用唯一一次
如果希望同一个对象,调用多次方法,那么必须给对象起个名字。3、匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】
强调:匿名内部类和匿名对象不是一回事!!!
*/
public class MyDemoMain {public static void main(String[] args) {MyInterface my = new MyInterface() {@Overridepublic void method() {System.out.println("使用匿名内部类实现方法!");}};my.method();new MyInterface() {@Overridepublic void method() {System.out.println("使用匿名内部类实现方法!");}}.method();}
}

Object类

/*
java.lang.Object类
类Object是类层次结构的根(最顶层)类,每个类都使用Object作为超类。
所有对象(包括数组)都实现了这个类的方法。1、当子类没有重写Object的toString方法时,直接打印对象的名字,其实就是调用toString方法2、equals在进行比较的时候,需要注意:基本数据类型:比较的是内容引用数据类型:比较的是地址值
Object类的equals方法默认比较的是两个对象的地址值,没有意义
所以我们需要重写equals方法,比较两个对象的属性值(...)对象的属性值一样,返回true;否则返回false;
问题:隐含这一个多态的问题Object obj = new Student(...);多态的弊端:无法使用子类特有的内容(属性,方法)解决:可以使用向下转型(强转)把Object类型转换为Student
*/
//自动生成的
@Overridepublic boolean equals(Object o) {if (this == o) return true;/** 下面的条件判断和obj instanceof student是一样的* 这里利用了反射技术,判断o是否为Student类型* */if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;if (age != student.age) return false;if (name != null ? !name.equals(student.name) : student.name != null) return false;return sex != null ? sex.equals(student.sex) : student.sex == null;}
//自动生成的高级版本(推荐使用这个)
@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age &&Objects.equals(name, student.name) &&Objects.equals(sex, student.sex);}
//自己写的
@Overridepublic boolean equals(Object obj) {if (obj == null)return false;if (obj == this)return true;if (obj instanceof Student){Student stu = (Student) obj;if (this.name == stu.getName() && this.age == stu.getAge() && this.sex == stu.getSex()){return true;}else {return false;}}return false;
//        return super.equals(obj);}
/*
这里说明一下Objects.equals方法:
Objects类的equals方法:对两个对象进行比较,防止空指针异常
public static boolean equals(Object a, Object b){return (a == b) || (a != null && a.equlas(b));
}
这样处理之后就不会因为null值出现空指针异常。
*/

毫秒值的概念和作用

/*
java.util.Date:表示日期和时间的类
类Date表示特定的瞬间,精确到毫秒。
毫秒:千分之一  1000毫秒 = 1秒
特定的瞬间:一个时间点,一刹那时间
2088-08-08 09:55:33:333 瞬间
2088-08-08 09:55:33:334 瞬间
2088-08-08 09:55:33:334 瞬间
...
毫秒值的作用:可以对时间和日期进行计算
2099-01-03 到 2088-01-01 中间一共有多少天
可以日期转换为毫秒进行计算,计算完毕,在把毫秒转换为日期把日期转换为毫秒:当前的日期:2088-01-01时间远点(0毫秒):1970年1月1日 00:00:00(英国格林威治)就是计算当前日期到时间远点之间一共经历了多少毫秒
注意:中国属于东八区,会把时间增加8个小时1970年1月1日 08:00:00把毫秒转换为日期:1天 = 24 x 60 x 60 = 86400秒 = 86400 x 1000 = 86400000毫秒
*/
public class Demo01Date {public static void main(String[] args) {//获取当前系统时间到1970 年 1 月 1 日 00:00:00经历了多少毫秒System.out.println(System.currentTimeMillis());System.out.println("=======================");demo01();System.out.println("=======================");demo02();System.out.println("========================");demo03();}/** Date 类的成员方法*       long getTime() 把日期转换为毫秒(相当于System.currentTimeMillis())*       返回自 1970 年 1月 1 日 00:00:00 GMT以来此 Date 对象表示的毫秒数。* */private static void demo03() {Date date = new Date();long time = date.getTime();System.out.println(time);}/** Date 类的带参构造方法:*      Date(long date):传递毫秒值,把毫秒转换为Date日期* */private static void demo02() {//此时传入的是0毫秒,以为这从1970 年1月1日过了0毫秒的日期Date date = new Date(11112312323L);System.out.println(date);}/** Date 类的空参数构造方法:*       Date()获取的就是当前系统的日期和时间* */private static void demo01() {Date date = new Date();System.out.println(date);}
}
DateFomat类&SimpleDateFormat类
/*
java.text.DateFormat是日期/时间格式化子类的抽象类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。
成员方法:String format(Date date) 按照指定的模式,把Date日期,格式化符合模式的字符串Date parse(String source)把符合模式的字符串,解析为Date日期
DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat的子类java.text.SimpleDateFormat extends DateFormat构造方法:SimpleDateFormat(String pattern) 用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数:String pattern:传递指定的模式模式:区分大小写的写对应的模式,会把模式替换为对应的日期和时间”yyyy-MM-dd HH:mm:ss“注意:模式中的字母不能更改,连接模式的符号可以改变
*/
public class Demo01DateFormat {public static void main(String[] args) {demo01();System.out.println("=====================");demo02();}private static void demo02() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");try {Date date = sdf.parse("2017年12月11日 14时06分12秒");System.out.println(date);} catch (ParseException e) {e.printStackTrace();}}private static void demo01() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = new Date();String text = sdf.format(date);System.out.println(date);System.out.println("============");System.out.println(text);}
}
Calendar类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CXwRyiDr-1636071520920)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210204172143448.png)]

常用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KYbQ70l-1636071520921)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210204172324009.png)]

System类的常用方法
/*
java.lang.System类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:public static long currentTimeMills():返回以毫秒为单位的当前时间。public static void arraycopy(Object src, int srcPos, Obeject dest, int destPos, int length):将数组中指定的数据拷贝到另一个数组中。
*/
StringBuilder类
/*
StringBuilder类字符串缓冲区,可以提高字符串的操作效率(看成一个长度可以变化的字符串)底层也是一个数组,但是没有被final修饰,可以改变StringBuilder在内存中始终是一个数组,占用空间少,效率高
如果超出了StringBuilder的容量,会自动扩容。构造方法:
public StringBuilder():构造一个空的StringBuilder容器。
pubic StringBuilder(String str):构造一个StringBuilder容器,并将字符串加进去。
*/
public static void main(String[] args) {StringBuilder bu1 = new StringBuilder();System.out.println(bu1);StringBuilder bu2 = new StringBuilder("abc");System.out.println(bu2);}
/*
StringBuilder常用的方法有2个:
public StringBuiler append(...):添加任意类型数据的字符串形式,并返回当前对象自身。
public String toString():将当前StringBuilder对象转换为String对象。
Public StringBuilder reverse(...):将参数字符串的内容反转,返回的仍然是对象自身。
*/public static void main(String[] args) {StringBuilder bu1 = new StringBuilder();System.out.println(bu1);StringBuilder bu2 = new StringBuilder("abc");System.out.println(bu2);bu1.append("sfdasdfa");System.out.println(bu1);System.out.println("=================");bu1.reverse();System.out.println(bu1);}/*
StringBuilder和String可以互相转化:String->StringBuilder:可以使用StringBuilder的构造方法StringBuilder(String str) 构造一个字符串生成器,并初始化为指定的字符串内容。StringBuilder->String:可以使用StringBuilder中的toString方法public String toString():将当前StringBuilder对象转化成String
*/String str = "hello";System.out.println("str:" + str);//String -> StringBuilderStringBuilder sb = new StringBuilder(str);sb.append(" world");System.out.println(sb);//StringBuilder -> StringString s = sb.toString();System.out.println(s);

包装类

/*
就是把基本数据类型包装起来,并添加一些方便处理基本数据内容的方法,这样的类就是包装类装箱:将基本数据类型包装(基本数据类型 -> 包装类)
拆箱:从包装类对象转换为对应的基本类型自动装箱与自动拆箱:基本类型的数据和包装类之间可以自动的相互转化JDK1.5之后出现的新特性自动装箱:直接把int类型的整数赋值给包装类自动拆箱:包装类无法直接参与计算,可以转化为基本类型进行计算
*/
基本类型与字符串类型的转换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrESeJUW-1636071520922)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210205104426473.png)]

Collection集合

数组:1、长度不可变2、可以存储基本数据类型,也可以存储对象
集合:1、长度可变2、不能存储基本数据类型,只能存储对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qjwfG7k-1636071520923)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210205110525321.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f1PDCMZI-1636071520925)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210205110757124.png)]

Collection集合常用方法
boolean add(E e);        向集合中添加元素
boolean remove(E e);    删除集合中的某个元素
void clear();           清空集合所有的元素
boolean contains(E e);  判断集合中是否包含某个元素
boolean isEmpty();      判断集合是否为空
int size();             获取集合长度
Object[] toArray();     将集合转成一个数组

Iterator迭代器

Iterator接口
/*
迭代:即Collection集合元素的通用获取方式。java.util.Iterator接口:迭代器(对集合进行遍历)
有两个常用的方法    boolean hasNext() 如果仍有元素可以迭代,则返回true。判断集合中还有没有下一个元素,有就返回true,没有就返回false E next() 返回迭代的下一个元素取出集合中的下一个元素Iterator迭代器是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象获取实现类的方法比较特殊,Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象。迭代器的使用步骤(重点):1、使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)2、使用Iterator接口中的方法hasNext判断还有没有下一个元素3、使用Iterator接口中的方法next取出集合中的下一个元素
*/public static void main(String[] args) {Collection<String> coll = new ArrayList<>();((ArrayList<String>) coll).add("姚明");((ArrayList<String>) coll).add("科比");((ArrayList<String>) coll).add("乔丹");((ArrayList<String>) coll).add("库里");Iterator<String> it = coll.iterator();while (it.hasNext()){System.out.println(it.next());}}
迭代器的实现原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r7ItrIsc-1636071520926)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210206102338674.png)]

foreach循环
/*
增强for循环:内部的实现原理其实是个Iterator迭代器(底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写是JDK1.5之后出现的新特性Collection<E> extends Iterable<E>:所有的单列集合都可以使用foreachpublic interface Iterable<T>实现这个接口允许对象成为“foreach”语句的目标。foreach:用来遍历集合和数组 格式:for(集合/数组的数据类型 变量名 : 集合名/数组名){sout(变量名);}
*/public static void main(String[] args) {demo01();demo02();}private static void demo02() {ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");for (String s: list) {System.out.println(s);}}private static void demo01() {int[] arr = {1,2,3,4,5};for (int a: arr) {System.out.println(a);}}

泛型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vgxaoaFj-1636071520927)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210206174921635.png)]

/*
创建集合对象,使用泛型
好处:1、避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型。2、把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候会报错)
弊端:泛型是什么类型,只能存储什么类型的数据创建集合对象,不使用泛型
好处:集合不使用泛型,默认的类型就是Object类型,可以存储任意类型的数据
弊端:不安全,会引发异常
*/
泛型的定义与使用

定义格式:

修饰符 class 类名<代表泛型的变量>{}
/*
* 定义一个含有泛型的类,模拟ArrayList集合
* */
public class GenericClass<ElemType> {private ElemType name;public ElemType getName() {return name;}public void setName(ElemType name) {this.name = name;}
}public static void main(String[] args) {GenericClass<String> gc = new GenericClass<>();gc.setName("hrg");System.out.println(gc.getName());System.out.println("=================================");GenericClass<Integer> gs = new GenericClass<>();gs.setName(1);System.out.println(gs.getName());}
/*
* 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
*
* 格式:
*   修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
*       方法体;
*   }
*
*   含有泛型的方法,在调用方法的时候确定泛型的数据类型
*   传递什么类型的参数,泛型就是什么类型
* */
public class GenericMethod {public <M> void method01(M m){System.out.println(m);}
}public static void main(String[] args) {GenericMethod gm = new GenericMethod();gm.method01(5);gm.method01(true);gm.method01(8.8);}
/*
* 定义含有泛型的接口
* */
public interface GenericInterface<I> {public abstract void method(I i);
}public class GenericInterfaceImpl implements GenericInterface<String> {@Overridepublic void method(String s) {System.out.println(s);}
}public class GenericInterfaceImpl2<I> implements GenericInterface<I> {@Overridepublic void method(I i) {System.out.println(i);}
}public class Demo02GenericInterface {public static void main(String[] args) {GenericInterfaceImpl gf = new GenericInterfaceImpl();gf.method("字符串");GenericInterfaceImpl2<Integer> gfm = new GenericInterfaceImpl2<>();gfm.method(10);GenericInterfaceImpl2<String> gfs = new GenericInterfaceImpl2<>();gfs.method("my love");}
}
泛型通配符
/*
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身无法使用。通配符基本使用
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往集合中存储数据
*/
public static void main(String[] args){Collection<Integer> list1 = new ArrayList<>();getElement(list1);Collection<String> list2 = new ArrayList<>();getElement(list2);
}
public static void getElement(Collection<?> coll){}
//?代表可以接收任意类型/*
泛型的通配符:?:代表任意的数据类型
使用方法:不能创建对象使用只能作为方法的参数使用
*/

容器类

容器类有两个类型:

​ 容器的类型以及元素的类型

​ ArrayList notes = new ArrayList;

常见的数据结构

数组

  • 查询快,增删慢

    • 原因:每次增加或删除都会重新创建一个新的数组

      int[] arr = new int[];
      

链表

  • 查询慢,增删快

    • 每次都必须从头开始查询

红黑树(tree)

​ 特点:

趋近于平衡树,查询速度非常的快,查询叶子节点最大次数和最小次数不能超过2倍

​ 约束:

  • 节点可以是红色的或者黑色的
  • 根节点是黑色的
  • 叶子节点(空节点)是黑色的
  • 每个红色的节点的字节点都是黑色的
  • 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同
自己查完之后再进行总结

集合

List集合
java.util.List接口 extends Collection接口
List接口的特点:1.有序的集合,存储元素和取出元素的顺序是一致的(存储123取出123)2.有索引,包含了一些带索引的方法3.允许存储重复的元素List接口中带索引的方法(特有):- public void add(int index, E element):将指定的元素,添加到该集合中的指定位置上- public E get(int index):返回集合中指定位置的元素。- public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素- public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素注意:操作索引的时候,一定要防止索引越界异常
ArrayList集合
该集合不是同步的,是多线程的,这样访问速度快
java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合
许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。
LinkedList集合
java.util.LinkedList集合 implements List接口
LinkedList集合的特点:1、底层是一个链表结构:查询慢,增删快2、里边包含了大量操作首尾元素的方法注意:使用LinedList集合特有的方法,不能使用多态- 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
Vector集合
Vector类可以实现可增长的对象数组。
与新collection实现不同,Vector是同步(单线程)的。
Set集合
Java.util.Set接口 extends Collection接口
Set接口的特点:1、不允许存储重复的元素2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历
HashSet集合
/*
java.util.HashSet集合 implements Set接口
HashSet特点:1、不允许存储重复的元素2、没有索引,没有带索引的方法,也不能使用普通的for循环遍历3、是一个无需的集合,存储的元素和取出元素的顺序有可能不一致4、底层是一个哈希表结构(查询的速度非常的快)哈希值:是一个十进制的整数,由系统随机给出(就是对象的地址值,是一个逻辑地址,是模拟出来得到地址,不是数据实际存储的物理地址
在Object类有一个方法,可以获取对象的哈希值
int hashCode() 返回该对象的哈希码值
hashCode方法的源码:public native int hashCode();native:代表该方法调用的是本地操作系统的方法
*/
public class Person extends Object {//Object类的hashCode方法是可以重写的@Overridepublic int hashCode() {return 1;}
}public class Demo03HashCode {public static void main(String[] args) {//Person类继承了Object类,所以可以调用hashCode方法Person p1 = new Person();int num1 = p1.hashCode();System.out.println(num1);//356573597Person p2 = new Person();int num2 = p2.hashCode();System.out.println(num2);//1735600054System.out.println("========================");System.out.println(p1);//com.hrg.demo.day03Set.Person@ 1540e19d (== 356573597)System.out.println(p2);//com.hrg.demo.day03Set.Person@ 677327b6 (== 1735600054)//哈希值相等,但是new出来的对象是不一定相等,因为哈希值不代表对象的物理地址,只有物理地址相等,对象才相等System.out.println(p1 == p2);//falseString s1 = new String("abc");String s2 = new String("abc");System.out.println(s1.hashCode());System.out.println(s2.hashCode());System.out.println(s1 == s2);//falseSystem.out.println(s1.equals(s2));//true}
}

哈希表结构[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6Nxd7YM-1636071520929)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210215113122474.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uKasmQYZ-1636071520930)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210215114949696.png)]

HashSet存储自定义类型元素

/*
set集合报错元素唯一:存储的元素(String,Integer,...,Student,Person),必须重写hashCode方法和equals方法要求:同名同年龄的人,视为同一个人,只能存储一次
*/
public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age &&Objects.equals(name, student.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}public class Demo03HashSetSaveStudent {public static void main(String[] args) {HashSet<Student> hset = new HashSet<>();Student t1 = new Student("鲁迅",18);Student t2 = new Student("鲁迅",18);Student t3 = new Student("鲁迅",20);hset.add(t1);hset.add(t2);hset.add(t3);System.out.println(hset);}
}
LinkedHashSet集合
/*
java.util.LinkedHashSet集合 extends HashSet集合
LinkedHashSet集合特点:底层是一个哈希表(数组+链表/红黑树)+链表:多了一条链表(记录元素的存储顺序),保证元素有序输出的元素和输入的元素顺序是一样的
*/
可变参数
/*
可变参数:是JDK1.5之后出现的新特性
使用前提:当方法的参数列表数据类型已经确定,但是参数的个数不确定,就可以使用可变参数。
使用格式:定义方法时使用修饰符 返回值类型 方法名(数据类型...变量名){}
可变参数的原理:可变参数底层就是一个数组,根据传递参数个数不同,会创建不同长度的数组,来存储这些参数传递的参数个数,可以是0个(不传递),1,2,...多个
*/  public class Demo03VarArgs {public static void main(String[] args) {//        int i = add();
//        int i = add(10);
//        int i = add(10,20);int i = add(10,20,30,40,50,60);System.out.println(i);}/** 定义计算(0-n)整数和的方法* 已知:计算整数的和,数据类型已经确定int* 但是参数的个数不确定,不知道要计算几个整数的和,就可以使用可变参数* add():就会创建一个长度为0的数组,new int[0]* add(10):就会创建一个长度为1的数组,new int[]{10};* add(10,20):就会创建一个长度为2的数组,new int[]{10,20};* ...* */public static int add(int ... arr){//        System.out.println(arr);
//        System.out.println(arr.length);int sum = 0;for (int i : arr) {sum += i;}return sum;}/*public static int add(int a,int b,int c){return a+b+c;}public static int add(int a,int b){return a+b;}*/
}/*
可变参数的注意事项1、一个方法的参数列表,只能有一个可变参数2、如果方法的参数有多个,那么可变参数必须写在参数列表的末尾
*/
public static void method(String a,double b,int...c){//方法体
}
//可变参数的特殊(终极)写法
public static void method(Object...obj){//方法体
}

Collections集合工具类方法

/*
java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:-public static <T> boolean allAll(Collection<T> c,T... elements):往集合中添加一些元素。-public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。
*/
public class Demo03CollectionsSet {public static void main(String[] args) {ArrayList<String> arr = new ArrayList<>();/*arr.add("a");arr.add("b");arr.add("c");arr.add("d");arr.add("e");System.out.println(arr);*///往集合中添加一些元素Collections.addAll(arr, "a", "b", "c", "d", "e");System.out.println(arr);//打乱集合中元素的顺序Collections.shuffle(arr);System.out.println(arr);}
}
/*
public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
public static <T> void sort(List<T> list, Comparator<? super T>):将集合中元素按照指定规则排序
注意:sort(List<T> list)使用前提被排序的集合里边存储的元素,必须实现Comparable,重写接口中的方法compareTo定义排序的规则sort(List<T> list, Comparator<? super T>):将集合中元素按照指定规则排序Comparator和Comparable的区别:Comparable:自己(this)和别人(参数)比较,自己需要实现Comparable接口,重写比较的规则compareTo方法Comparator:相当于找一个第三方的裁判,比较两个
*/
public class Demo03CollectionsSort {public static void main(String[] args) {ArrayList<Employee> list = new ArrayList<>();list.add(new Employee("张三",18));list.add(new Employee("李四",16));list.add(new Employee("王五",19));System.out.println(list);//使用带一个参数的/*Collections.sort(list);System.out.println(list);*///使用带两个参数的/** o1 - o2 : 升序* o2 - o1 : 降序* */Collections.sort(list, new Comparator<Employee>() {@Overridepublic int compare(Employee o1, Employee o2) {return o1.getAge() - o2.getAge();}});System.out.println(list);}
}

Map

Map的常用子类

/*java。util.Map<k,v>集合(实现不同步,多线程,速度快)Map集合的特点:1、Map集合是一个双列集合,一个元素包含两个值(一个key,一个value)2、Map集合中的元素,key和value的数据类型可以相同也可以不同3、Map集合中的元素,key是不允许重复的,value是可以重复的4、Map集合中的元素,key和value是一一对应的   java.util.HashMap<k,v>集合 implements Map<k,v>接口HashMap集合的特点:1、HashMap集合底层是哈希表:查询的速度特别的快JDK1.8之前:数组+单向链表JDK1.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询速度2、HahshMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致java.util.LinkedHashMap<k,v>集合 extends HashMap<k.v>集合LinkedHashMap的特点:1、LinkedHahshMap集合底层是哈希表+链表2、LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是一致的
*/

Map接口中的常用方法

/*
public V put(K key,V value):把指定的健与指定的值添加到Map集合中
public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值
public V get(Object key):根据指定的键,在Map集合中获取对应的值
boolean containsKey(Object key):判断集合中是否包含指定的键
public Set<K> keySet():获取Map集合中所有的键,存储到Set集合中
public Set<Map.Entry<k,v>> entrySet():获取到Map集合中所有的键值对对象的集合(Set集合)
*/
public class Demo04Map {public static void main(String[] args) {//        show01();
//        show02();
//        show03();show04();}/**boolean containsKey(Object key):判断集合中是否包含指定的键*   包含返回true,不包含返回false* */private static void show04() {Map<String,Integer> map = new HashMap<>();map.put("hrg",22);map.put("hrh",15);map.put("zh",45);boolean hrg = map.containsKey("hrg");System.out.println(hrg);boolean hhh = map.containsKey("hhh");System.out.println(hhh);}/** public V get(Object key):根据指定的键,在Map集合中获取对应的值*   返回值:v*       key存在,返回对应的value*       key不存在,返回null* */private static void show03() {Map<String,Integer> map = new HashMap<>();map.put("hrg",22);map.put("hrh",15);map.put("zh",45);map.put("hbd",47);Integer hrg = map.get("hrg");System.out.println(hrg);}/** public V remove(Object key):把指定的键所对应的键值对元素在Map集合中删除,返回被删除元素的值。*   返回值:v*       key存在,v返回被删除的value值*       key不存在,v返回null值* */private static void show02() {Map<String,Integer> map = new HashMap<>();map.put("hrg",22);map.put("hrh",15);map.put("zh",45);map.put("hbd",47);System.out.println(map);System.out.println("==========================");Integer hrg = map.remove("hrg");System.out.println(hrg);System.out.println(map);System.out.println("===========================");Integer hhh = map.remove("hhh");System.out.println(hhh);System.out.println(map);}/** public V put(K key,V value):把指定的健与指定的值添加到Map集合中*   返回值:v*       存储键值对的时候,key不重复,返回值v是null*       存储键值对的时候,key重复,会使用新的value替换map中之前的value,返回被替换的value值* */private static void show01() {Map<String,String> map = new HashMap<>();String v1 = map.put("hrg", "帅哥");System.out.println(v1);map.put("hrh","丑逼");map.put("zh","妈妈");map.put("hbd","老爸");System.out.println(map);}
}//keySet()方法的使用:
public class Demo04KeySet {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("hrg",22);map.put("hrh",16);map.put("hbd",48);System.out.println(map);Set<String> str = map.keySet();Iterator<String> it = str.iterator();while(it.hasNext()){String s = it.next();System.out.println(map.get(s));}//和上面的while是一样的效果for (String s : str) {Integer num = map.get(s);System.out.println(num);}}
}
//entrySet()方法
/*
* Map集合中的方法:
*   Set<Map.Entry<K,V>> entrySet() 返回此映射中包含的映射关系的set视图
*
*   实现步骤:
*       1、使用Map集合中的方法entrySet(),把Map集合中多个Entry对象取出来,存储到一个Set集合中
*       2、遍历Set集合,获取每一个Entry对象
*       3、使用Entry对象中的方法getKey()和getValue()获取键与值
* */
public class Demo04entrySet {public static void main(String[] args) {Map<String,Integer> map = new HashMap<>();map.put("hrg",22);map.put("hrh",16);map.put("hbd",48);map.put("zh",46);System.out.println(map);Set<Map.Entry<String, Integer>> entries = map.entrySet();Iterator<Map.Entry<String, Integer>> it = entries.iterator();while(it.hasNext()){//            System.out.println(it.next());Map.Entry<String, Integer> en = it.next();String key = en.getKey();System.out.print(key);Integer va = map.get(key);System.out.println(" " + va);}//上面的while和下面的for效果是一样的for(Map.Entry<String,Integer> entry : entries){String key = entry.getKey();System.out.print(key);Integer va = map.get(key);System.out.println(" " + va);}}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4BlWzp7M-1636071520931)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210220111424192.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7VhDklxh-1636071520933)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210220114519155.png)]

HashMap存储自定义类型键值

/*
*   HashMap存储自定义类型键值:
*   Map集合保证key是唯一的:
*       作为key的元素,必须重写hashCode方法和equals方法,以保证key唯一
* */
public class Demo04HashMapSavePerson {public static void main(String[] args) {//        show01();show02();}/**   HashMap存储自定义类型键值*   key:Person类型*       Person类必须重写hashCode()和equals()方法,以保证key唯一*   value:String类型*       可以重复* */private static void show02() {HashMap<Person,String> map = new HashMap<>();map.put(new Person("丑逼",18), "美国");map.put(new Person("帅逼",18), "中国");map.put(new Person("垃圾",18), "印度");map.put(new Person("丑逼",18), "英国");Set<Map.Entry<Person, String>> set = map.entrySet();for (Map.Entry<Person, String> entry : set) {Person key = entry.getKey();String value = entry.getValue();System.out.println(key+"-->"+value);}}/**   HashMap存储自定义类型键值*       key:String类型*           String类型重写hashCode()和equals()方法,保证key的唯一*       value:Person类型*           value可以重复(同名同年龄的人视为同一个)* */private static void show01() {Map<String,Person> map = new HashMap<>();map.put("北京", new Person("张三",18));map.put("上海", new Person("李四",19));map.put("广州", new Person("王五",20));map.put("北京", new Person("赵六",18));Set<String> strings = map.keySet();for (String s : strings) {Person value = map.get(s);System.out.println(s + "-->" + value);}}
}

LinkedHashMap集合

/*java.util.LinkedHashMap<K,V> extends HashMap<K,V>Map 接口的哈希表和链表列表实现,具有可预知的迭代顺序。底层原理:哈希表+链表(记录元素的顺序)
*/
public class Demo04LinkedHashMap {public static void main(String[] args) {HashMap<String,String> map = new HashMap<>();map.put("a","a");map.put("c","c");map.put("b","b");map.put("a","d");System.out.println(map);//key不允许重复,无序LinkedHashMap<String,String> linkedmap = new LinkedHashMap<>();linkedmap.put("a","a");linkedmap.put("c","c");linkedmap.put("b","b");linkedmap.put("a","d");System.out.println(linkedmap);//key不允许重复,有序}
}

HashTable

/*java.util.Hashtable<k,V>集合 implements Map<K,V>接口Hashtable:底层也是一个哈希表,是一个线程安全的集合,是单线程集合,速度慢HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快HashMap集合(之前学的所有的集合):可以存储null值,null键Hashtable集合,不能存储null值,null键Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了Hashtable的子类Properties依然活跃在历史舞台Properties集合是一个唯一和IO流相结合的集合
*/public class Demo04HashTable {public static void main(String[] args) {HashMap<String,String> map = new HashMap<>();map.put(null, "a");map.put("b", null);map.put(null, null);System.out.println(map);Hashtable<String,String> table = new Hashtable<>();//table.put("a",null);//错误,会报空指针异常//table.put(null,"a");//错误,会报空指针异常//table.put(null,null);//错误,会报空指针异常table.put("a","a");System.out.println(table);}
}

JDK9对集合添加的优化

/*JDK9的新特性:List接口,Set接口,Map接口:里边增加了一个静态的方法of,可以给集合一次性添加多个元素static <E> List<E> of (E... elements)使用前提:当集合中存储的元素的个数已经确定了,不在改变时使用注意:1、of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类(意味着不适用于HashMap,HashSet)2、of方法的放回置是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常3、Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常
*/

Debug追踪

Debug调试程序:可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
使用方式:在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)右键,选择Debug执行程序程序就会停留在添加的第一个断点处
执行程序:Step Over:逐行执行程序Step into:进入到方法中Step out:跳出方法Resume Program:跳到下一个断点,如果没有下一个断点,那么就结束程序Console:切换到控制台

异常体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OiLiiFW-1636071520934)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210224175506542.png)]

异常的根类是java.lang.Throwable,其下有两个子类:
1、java.lang.Error:错误(必须修改源代码)
2、java.lang.Exception:编译期异常,进行编译(写代码)java程序出现的问题RuntimeException:运行期异常,Java程序运行过程中出现的问题,是Exception下面的子类,
平时所指的异常是java.lang,Exception
JVM处理异常的过程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2iFyn45H-1636071520935)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210224181255383.png)]

/*
throw关键字
作用:可以使用throw关键字在指定的方法中抛出指定的异常
使用格式:throw new xxxException("异常产生的原因")
注意:1、throw关键字必须写在方法的内部2、throw关键字后边new的对象必须是Exception或者Exception的子类对象3、throw关键字抛出指定的异常对象,我们就必须处理这个异常对象throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try...catch
*//*
Objects类中的静态方法
public static <T> T requireNonNull(T obj):查看指定引用对象是不是null.
*//*
throws关键字
在方法声明的时候使用,抛出异常
*//*
Throwable类中定义了3个异常处理的方法String getMessage() 返回此throwable的简短描述String toString() 返回此throwable的详细消息字符串Void printStackTrace() JVM打印异常对象,默认此方法,打印的异常信息是最全面的
*//*
finally关键字1、finally不能单独使用,必须和try一起使用2、finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)
*/
异常的注意事项
/*
多个异常使用捕获又该如何处理呢?1、多个异常分别处理2、多个异常一次捕获,多次处理3、多个异常一次捕获一次处理
*//*
子父类的异常:- 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。- 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
*/
public class Fu {public void show01() throws NullPointerException,IndexOutOfBoundsException{};public void show02() throws NullPointerException,IndexOutOfBoundsException{};public void show03() throws NullPointerException,IndexOutOfBoundsException{};public void show04(){};
}class Zi extends Fu{//子类重写父类方法时,抛出父类异常的子类public void show01() throws NullPointerException,IndexOutOfBoundsException{};//子类重写父类方法时,抛出父类异常的子类public void show02() throws ArrayIndexOutOfBoundsException{};//子类重写父类方法时,不抛出异常public void show03(){};//父类没有抛出异常,子类重写父类方法时也不可抛出异常public void show04(){try {throw new Exception("编译期运行异常");} catch (Exception e) {e.printStackTrace();}}
}
自定义异常处理
/*自定义异常类:java提供的异常类,不够我们使用,需要自己定义一些异常类格式:public class xxxException extends Exception | RuntimeException{添加一个空参数的构造方法添加一个带异常信息的构造方法}注意:1、自定义异常类一般都是以Exception结尾,说明该类是一个异常类2、自定义异常类,必须继承Exception或者RuntimeException继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try...catch继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
*/

Java多线程

多线程基础知识
并发与并行并发:程序交替执行并行:程序同时执行
线程与进程:进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个进程可以包含多个线程线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aWnAExKu-1636071520936)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210225172912158.png)]

线程的内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OrmZJpzv-1636071520937)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210228190135138.png)]

public static void main(String[] args){MyThead mt = new MyThead();mt.run();mt.start();
}
/*注意:如果调用mt.run()方法执行,此时是单线程执行,是由main方法调用的run()方法而mt.start()方法则不同,调用该方法可以重新开辟一个栈空间用来执行run()方法,因此此时是多线程执行
*/
Thread类
/*构造方法:public Thread():分配一个新的线程对象public Thread(String name):分配一个指定名字的新的线程对象public Thread(Runnable target):分配一个带有指定目标新的线程对象public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字
*//*常用方法:public String getName():获取当前线程名称public void start():导致此线程开始执行;Java虚拟机调用此线程的run方法public void run():此线程要执行的任务在此处定义代码public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)public static Thread currentThread():返回对当前正在执行的线程对象的引用
*//*创建线程的方法:1、继承Thread类方式2、实现Runnable接口方式实现步骤:1、创建一个Runnable接口的实现类2、在实现类中重写Runnable接口的run方法,设置线程任务3、创建一个Runnable接口的实现类对象4、创建Thread类对象,构造方法中传递Runnable接口的实现类对象5、调用Thread类中的start方法,开启新的线程执行run方法
*//*实现Runnable接口创建多线程程序的好处:1、避免了单继承的局限性一个类只能继承一个类,类继承了Thread类就不能继承其他的类实现了Runnable接口,还可以继承其他的类,实现其他的接口2、增强了程序的扩展性,降低了程序的耦合性(解耦)实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)实现类中,重写了run方法:用来设置线程任务创建Thread类对象,调用start方法:用来开启新线程
*/
使用synchronized实现代码同步
public class RunnableImpl implements Runnable {private static int ticket = 100;//定义一个锁对象,利用synchronized来进行线程同步Object obj = new Object();@Overridepublic void run() {while(true){//这是第一种方法,利用锁对象,将共享资源同步/*synchronized (obj){if (ticket > 0){System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "号票");ticket--;}else{break;}}*/payTicket();}}//第二种,使用同步代码块,public synchronized void payTicket(){//        synchronized (this){if (ticket > 0){System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "号票");ticket--;}
//        }}/*第三种,使用静态同步代码块,此时ticket也必须是静态变量静态的同步对象就不嫩是this了,this是创建对象之后产生的,静态方法优于对象静态方法的锁对象是本类的class属性--》class文件对象(反射)* */public static /*synchronized*/ void payTicketStatic(){synchronized (RunnableImpl.class){if (ticket > 0){System.out.println(Thread.currentThread().getName() + "-->正在卖第" + ticket + "号票");ticket--;}}}
}public class Demo06Ticket {public static void main(String[] args) {RunnableImpl able = new RunnableImpl();Thread t1 = new Thread(able);Thread t2 = new Thread(able);Thread t3 = new Thread(able);t1.start();t2.start();t3.start();}
}
使用Lock锁实现代码同步
/*解决线程安全问题的第三种方案:Lock锁java.util.concurrent.locks.Lock接口Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作Lock接口中的方法:void lock() 获取锁void unlock() 释放锁java.util.concurrent.locks.ReentrantLock implements Lock接口使用步骤:1、在成员位置创建一个ReentrantLock对象2、在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁3、在可能会出现安全问题的代码后调用Lock接口中的方法unlock获取锁
*/
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class RunnableLock implements Runnable{private int ticket = 100;//创建一个ReentrantLock锁对象Lock l = new ReentrantLock();@Overridepublic 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();}}}}
}
线程状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7WZQLWFI-1636071520938)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210302135306251.png)]

Wait And Notify方法
/*等待唤醒案例:线程之间的通信创建一个顾客线程:告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITTING状态(无限等待)创建一个老板线程:花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子注意:顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行同步使用的锁对象必须保证唯一只有锁对象才能调用wait和notify方法Object类中的方法void wait() 在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。void notify() 唤醒在此对象监视器上等待的单个线程会继续执行wait之后的代码* */
public class Demo06WaitAndNotify {public static void main(String[] args) {//创建一个锁对象,保证唯一Object obj = new Object();//创建一个顾客线程new Thread(){@Overridepublic void run() {//必须保证线程同步,保证等待和唤醒的线程只能有一个执行synchronized (obj){System.out.println("顾客付钱给老板等待老板的包子");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}//唤醒之后的代码System.out.println("顾客开始吃包子");}}}.start();new Thread(){@Overridepublic void run() {//设置做包子的时间try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}//必须保证线程同步,保证等待和唤醒的线程只能有一个执行synchronized (obj){System.out.println("老板5秒钟之后做好包子给顾客");obj.notify();}}}.start();}
}/*进入到TimeWaiting(计时等待)有两种方式1、使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态2、使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态
*//*调用wait和notify方法需要注意的细节1、wait方法和notify方法必须要由同一个锁对象调用2、wait方法和notify方法式属于Object类。3、wait方法与notify方法必须要在同步代码块或者式同步函数中使用。
*/

线程池

线程池的原理

线程池:容器——>集合(ArrayList,HashSet,LinkedList,HashMap)

线程池的原理:当程序第一启动的时候,创建多个线程,保存到一个集合中当我们想要使用线程的时候,就可以从集合中去出来线程使用Thread t = list.remove(0);Thread t = linked.removeFirst();当我们使用完线程,需要将线程归还给线程池list.add(t);linked.addLast(t);

在JDK1.5之后,JDK内置了线程池,我们可以直接使用;

线程池的使用
/*线程池: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 任务用于执行关闭/销毁线程的方法void shutdown()线程池的使用步骤:1、使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定数量的线程池2、创建一个类,实现Runnable接口,重写run方法,设置线程任务3、调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
*/
public class Demo06ThreadPool {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);
//        MyThreadImpl mti = new MyThreadImpl();//线程池会一直开启,使用完了之后,会自动把线程归还给线程池,线程可以继续使用
//        executorService.submit(mti);executorService.submit(new MyThreadImpl());executorService.submit(new MyThreadImpl());executorService.submit(new MyThreadImpl());}
}public class MyThreadImpl implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-->"+"这是利用线程池设置的线程任务");}
}

Lambda表达式

函数式编程思想
只要能获得到结果,谁去做的,怎么做的都不重要,重视的是结果
lambda表达式的使用
/*使用Runnable接口的方式实现多线程程序和使用lambda表达式
*/
public class Demo06Runnable {public static void main(String[] args) {RunnableImpl able = new RunnableImpl();new Thread(able).start();Runnable a = new Runnable(){@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"设置的线程任务");}};new Thread(a).start();//lambda表达式简化代码new Thread(()->{System.out.println(Thread.currentThread().getName()+"设置的线程任务");}).start();}
}/*lambda表达式的标准格式:由三部分组成:a、一些参数b、一个箭头c、一段代码格式:(参数列表) -> {一些重写方法的代码}
*/
public class Demo06Cook {public static void main(String[] args) {//调用invokeCook方法,参数是Cook接口的匿名内部类对象invokeCook(new Cook() {@Overridepublic void makeFood() {System.out.println("吃饭了");}});//使用lambda表达式,简化匿名内部类的书写invokeCook(()->{System.out.println("吃屎了");});}//定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFoodpublic static void invokeCook(Cook cook){cook.makeFood();}
}//带参数的lambda表达式
public class Demo06Calculator {public static void main(String[] args) {//使用匿名内部类调用的方式/*mycalc(10, 20, new Calculator() {@Overridepublic int calc(int a, int b) {return a+b;}});
*///使用lambda表达式mycalc(3, 4, (int a, int b)->{return a+b;});}public  static void mycalc(int a, int b, Calculator ca){    //这里的Calculator是自己设置的接口,并且定义了一个calc抽象方法int sum = ca.calc(a,b);System.out.println(sum);}
}/*lambda表达式:可推导,可以省略凡是根据上下文推导出来的内容,可以省略不写可以省略的内容:1、(参数列表):括号中参数列表的数据类型,可以省略不写2、(参数列表):括号中的参数如果只有一个,那么类型和()都可以省略3、(一些代码):如果()中的代码只有一行,无论是否有返回值,都可以省略({},return,分号)注意:要省略{},return,分号必须一起省略
*/
lambda的使用前提
/*lambda的语法简洁,但是仍有需要注意的地方:1、使用lambda必须具有接口,且要求接口中有且仅有一个抽象方法。无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。2、使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为lambda对应的而接口类型,才能使用Lambda作为该接口的实例。备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
*/

File类

/*java.io.File类文件和目录路径名的抽象表示形式。java把电脑中的文件和文件夹(目录)封装为一个File类,我们可以使用File类对文件和文件夹进行操作我们可以使用File类的方法创建一个文件/文件夹删除文件/文件夹获取文件/文件夹判断文件/文件夹是否存在对文件夹进行遍历获取文件的大小File类是一个与系统无关的类,任何的操作系统都可以使用这个类中的方法重点:记住三个单词file:文件directory:文件夹/目录path:路径
*/
import java.io.File;public class Demo07File {/*static String pathSeparator    与系统有关的路径分隔符,为了方便,他被表示为一个字符串static char pathSeparatorChar   与系统有关的路径分隔符static String separator     与系统有关的默认名称分隔符,static char separatorChar   与系统有关的默认名称分隔符*/public static void main(String[] args) {String pathSeparator = File.pathSeparator;System.out.println(pathSeparator);char pathSeparatorChar = File.pathSeparatorChar;System.out.println(pathSeparatorChar);String separator = File.separator;System.out.println(separator);char separatorChar = File.separatorChar;System.out.println(separatorChar);}
}/*File类的构造方法有四个:可以查阅API文档找到其定义和使用方法
*/import java.io.File;public class Demo07FileMethod {/*常用方法:public String getAbsolutePath():返回此File的绝对路径名字符串public String getPath():将此File转换为路径名字符串public String getName():返回由此File表示的文件或目录的名称public long length():返回由此File表示的文件的长度(文件的大小),以字节为单位*/public static void main(String[] args) {File file = new File("a.txt");String absolutePath = file.getAbsolutePath();System.out.println(absolutePath);//D:\idea\InterlliJ IDEA project\exercisedemo\a.txtSystem.out.println("========================");String path = file.getPath();System.out.println(path);//a.txtSystem.out.println("========================");String name = file.getName();System.out.println(name);//a.txtSystem.out.println("========================");long length = file.length();System.out.println(length);//0}
}/*判断功能的方法:public boolean exists():此File表示的文件或者目录是否实际存在public boolean isDirectory:此File表示的是否为目录public boolean isFile:此File表示的是否为文件
*//*File的创建和删除方法:public boolean createNewFile():当且仅当具有该名称的文件尚不存在时,创建一个新的空文件;public boolean delete():删除由此File表示的文件或者目录public boolean mkdir():创建由此File表示的目录(创建单级文件夹)public boolean mkdirs():创建由此File表示的目录,包括任何必需但不存在的父目录(创建单级或多级文件夹)
*//*File类遍历(文件夹)目录功能:public String[] list():返回一个String数组,表示该File目录中的所有子文件或目录public File[] listFile():返回一个File数组,表示该File目录中的所有的子文件或目录*/
路径
  • 绝对路径:是一个完整的路径,以盘符开始的路径
  • 相对路径:是一个简化的路径,相对指的是相对于当前项目的根目录,(如C:/study/123.txt可以简化为123.txt)
  • 注意:
    • 路径不区分大小写
    • 路径中的文件名称分隔符windows使用反斜杠,但是反斜杠是转移字符,因此需要用两个反斜杠才能表示一个反斜杠

递归

分类:

  • 直接递归
  • 间接递归

注意事项:

  • 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
  • 在递归中虽然有限定条件,但是递归次数不能太多,否则也会发生栈内存溢出
  • 构造方法,禁止递归
利用递归实现文件目录的层级打印(涉及到文件过滤器这个新的知识点)
package com.hrg.demo.day08digui;import java.io.File;/*
* 遍历目录文件及其子文件目录(只要以.txt结尾的)
*
* 我们可以使用过滤器来实现
* 在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,在方法中自己定义过滤的规则
*
* */
public class Demo08Filter {public static void main(String[] args) {File file = new File("D:\\testforidea");getAllFile(file);}/*定义一个方法,参数传递File类型的目录方法中对目录进行遍历*/public static void getAllFile(File dir){//        System.out.println(dir);File[] files = dir.listFiles(new FileFilterImpl());for (File file : files) {if (!file.isDirectory()){System.out.println(file);}else {getAllFile(file);}}}
}//过滤器的实现类
public class FileFilterImpl implements FileFilter {@Overridepublic boolean accept(File pathname) {if (pathname.isDirectory()){return true;}return pathname.toString().toLowerCase().endsWith(".txt");}
}

IO

概念:

i: input
o:output
流:数据(字符,字节) 1个字符=2个字节   1个字节=8个二进制位
输入:把硬盘中的数据读取到内存中使用
输出:把内存中数据写入到硬盘中保护
输入流 输出流
字节流 InputStream OutputStream
字符流 Reader Writer

字节输出流

FileOutputStream
import java.io.FileOutputStream;
import java.io.IOException;/*java.io.OutputStream:此抽象类是表示输出字节流的所有类的超类定义了一些子类共性的成员方法:public void close(): 关闭此输出流并释放与此流相关联的任何系统资源public void flush(): 刷新此输出并强制任何缓冲的字节被写出public void write(byte[] b): 将b.length字节从指定的字节数组写入此输处流public void write(byte[] b,int off,int len): 从指定的字节数组写入len字节,从偏移量off开始输出到此输出流public abstract void write(int b): 将指定的字节输出流java.io.FileOutputStream extends OutputStreamFileOutputStream: 文件字节输出流作用:把内存中的数据写入到硬盘的文件中构造方法:FileOutputStream(String name) 创建一个向具有指定文件中写入数据的输出文件夹FileOutputStream(File file) 创建一个向指定File对象表示的文件中写入数据的文件输出流。参数:String name : 目的地是一个文件的路径File file :目的地是一个文件构造方法的作用:1、创建一个FileOutputStream对象2、会根据构造方法中传递的文件/文件路径,创建一个空的文件3、会把FileOutputStream对象指向创建好的文件写入数据的原理(内存——>硬盘)Java程序-->JVM(Java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中字节输出流的使用步骤:1、创建一个FileOutputStream对象,构造方法中传递写入数据的目的地2、调用FileOutputStream对象中的方法write,把数据写入到文件中3、释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)*/
public class Demo09OutputStream {public static void main(String[] args) throws IOException {//1、创建一个FileOutputStream对象,构造方法中传递写入数据的目的地FileOutputStream fos = new FileOutputStream("D:\\testforidea\\c.txt");//2、调用FileOutputStream对象中的方法write,把数据写入到文件中fos.write(97);//3、释放资源(流使用会占用一定的内存,使用完毕要把内存清空,提供程序的效率)fos.close();}
}/*一次写多个字节的方法:
*/
public class Demo09OutputStreamMore {public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("D:\\testforidea\\e.txt");/*public void write(byte[] b): 将b.length字节从指定的字节数组写入此输出流一次写多个字节:如果写的第一个字节是正数(0-127),那么显示的时候会查询ASCII码表如果写的第一个字节是负数,那么第一个字节会和第二个字节,两个字节组成一个中文显示,查询系统默认码表(GBK)*///byte[] bytes = {49, 48, 48};//byte[] bytes = {-65, -49, -48, 48}; //-65和-49会组成一个中文,-48和48会组成一个中文byte[] bytes = {69, 78, 79, 76, 67, 66};/*public void write(byte[] b,int off, int len): 将字节数组的部分写入此输出流off:byte数组的开始位置len:从off位置开始多长*/fos.write(bytes,1,2);byte[] bytes1 = "你好".getBytes();/*在GBK中,中文是两个字节来表示在UTF-8中,中文是用三个字节来表示*/System.out.println(Arrays.toString(bytes1));fos.write(bytes1);fos.close();}
}/*文件按数据的追加/续写:使用两个参数的构造方法FileOutputStream(String name, boolean append) 创建一个向具有指定name的文件中写入数据的输出文件流FileOutputStream(File file, boolean append) 创建一个向指定File对象表示的文件中写入数据的文件输出流参数:String name,File file:写入数据的目的地boolean append: 追加写开关true:创建对象不会覆盖原文件,继续在文件的末尾追加写数据false:创建一个新文件,覆盖源文件文件中写入的数据进行换行:不同系统的换行符:Windows:\r\nLinux:/nmac:/n
*/
public class Demo09OutputStreamAdd {public static void main(String[] args) throws IOException {/*当开启true了之后,上一次程序运行的结果就一直保留在里边了,但是如果重新置为false的话,就会覆盖源文件*/FileOutputStream fos = new FileOutputStream("D:\\testforidea\\d.txt",true);byte[] bytes = "你好啊".getBytes();for (int i = 0; i < 10; i++) {fos.write(bytes);fos.write("\r\n".getBytes());}byte[] bytes1 = {47, 67, 78, 90, 12};fos.write(bytes1);fos.close();}
}
FileInputStream
package com.hrg.demo.day09IO;import java.io.FileInputStream;
import java.io.IOException;/*java.io.InputStream:字节输入流此抽象类是表示输入流的所有类的超类。定义了所有子类共性的方法:int read() 从输入流中读取数据的下一个字节int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中void close() 关闭此输入流并释放与该流关联的所有系统资源。java.io.FileInputStream extends InputStreamFileInputStream: 文件字节输入流作用:把硬盘文件中的数据,读取到内存中使用构造方法:FileInputStream(String name)FileinputStream(File file)参数:读取文件的数据源String name:文件的路径File file:文件构造方法的作用:1、会创建一个FileInputStream对象2、会把FileInputStream对象指定构造方法中要读取的文件读取数据的原理(硬盘-->内存)java程序-->JVM-->OS-->OS读取数据的方法-->读取文件字节输入流的使用步骤:1、创建FileInputStream对象,构造方法中绑定主要读取的数据源2、使用FileInputStream对象中的方法read,读取文件3、释放资源
*/
public class Demo09InputStream {public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("D:\\testforidea\\c.txt");/*当继续往下读的时候,下一次读就会读取下一个字节,而如果没有字节了,就返回-1*//*int one = fis.read();System.out.println(one);int two = fis.read();System.out.println(two);int three = fis.read();System.out.println(three);int four = fis.read();System.out.println(four);*//*这里必须要用一个变量(len)来接收fis.read,不能直接写成while(fis.read() != -1){System.out.println(fis.read());}这样会导致最后的结果出问题*/int len = 0;while ((len=fis.read()) != -1){System.out.println(len);System.out.println((char) len);}fis.close();}
}/*字节输入流一次读取多个字节的方法:int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储到缓冲区数组b中明确两件事情:1、方法的参数byte[]的作用起到缓冲作用,存储每次读取到的多个字节数组的长度一般定义为1024(1kb)或者1024的整数倍2、方法的返回值int是什么每次读取的有效字节个数String类的构造方法String(byte[] bytes):把字节数组转换为字符串String(byte[] bytes,int offset,int length):把字节数组的一部分转换为字符串,offset:数组索引的开始位置
*/
public class Demo09InputStreamMore {public static void main(String[] args) throws IOException {//创建FileInputStream对象,构造方法中绑定要读取的数据源FileInputStream fis = new FileInputStream("D:\\testforidea\\c.txt");//使用FileInputStream对象中的方法read读取文件/*byte[] bytes = new byte[2];int len = fis.read(bytes);System.out.println(len);
//        System.out.println(Arrays.toString(bytes));System.out.println(new String(bytes));len = fis.read(bytes);System.out.println(new String(bytes));System.out.println(len);len = fis.read(bytes);System.out.println(new String(bytes));System.out.println(len);*/byte[] bytes = new byte[1024];int len = 0;while ((len = fis.read(bytes)) != -1){System.out.println(new String(bytes,0,len));}//释放资源fis.close();}
}//复制文件
/*文件复制练习:一读一写明确:数据源:c:\\xxx.txt数据目的地:d:\\xxx.txt文件复制的步骤:1、创建一个字节输入流对象,构造方法中绑定要读取的数据源2、创建一个字节输出流对象,构造方法中绑定要写入的目的地3、使用字节输入流对象中的方法read读取文件4、使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中5、释放资源
*/
public class Demo09CopyFile {public static void main(String[] args) throws IOException {//1、创建一个字节输入流对象,构造方法中绑定要读取的数据源FileInputStream fis = new FileInputStream("d:\\testforidea\\c.txt");//2、创建一个字节输出流对象,构造方法中绑定要写入的目的地FileOutputStream fos = new FileOutputStream("d:\\testforidea\\a\\a.txt");/*//一次读入一个字节的方式int len = 0;while ((len = fis.read()) != -1){fos.write(len);}*///优化:使用数组缓冲读取多个字节,写入多个字节的方式byte[] bytes = new byte[1024];//使用字节输入流对象中的方法read读取文件int len = 0;while ((len = fis.read(bytes)) != -1){fos.write(bytes,0,len);}//释放资源,先释放写的资源,再释放读的资源,如果写完了,那么一定是读完了fos.close();fis.close();}
}

JDK7和JDK9流中异常的处理

//JDK7
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo10JDK7 {/*JDK7新特性:在try的后边可以增加一个(),在括号中可以定义流对象那么这个流对象的作用域就在try中有效try中的代码执行完毕,会自动把流对象释放,不用写finally格式:try(定义流对象;定义流对象.....){可能会产生异常的代码块}catch(异常类变量 变量名){异常的处理逻辑}* */public static void main(String[] args) {try(//1、创建一个字节输入流对象,构造方法中绑定要读取的数据源FileInputStream fis = new FileInputStream("d:\\testforidea\\c.txt");//2、创建一个字节输出流对象,构造方法中绑定要写入的目的地FileOutputStream fos = new FileOutputStream("d:\\testforidea\\a\\a.txt");){//一次读入一个字节的方式int len = 0;while ((len = fis.read()) != -1){fos.write(len);}}catch (IOException e){System.out.println(e);}}
}//JDK9
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo11JDK9 {/*JDK9的新特性:try的前面可以定义流对象在try后边的()中可以直接引入流对象的名称(变量名)在try代码执行完毕之后,流对象也可以释放掉,不用写finally格式:A a = new A();B b = new B();try(a;b){}catch(){}* */public static void main(String[] args) throws FileNotFoundException {//1、创建一个字节输入流对象,构造方法中绑定要读取的数据源FileInputStream fis = new FileInputStream("d:\\testforidea\\c.txt");//2、创建一个字节输出流对象,构造方法中绑定要写入的目的地FileOutputStream fos = new FileOutputStream("d:\\testforidea\\a\\a.txt");try(fis;fos){//一次读入一个字节的方式int len = 0;while ((len = fis.read()) != -1){fos.write(len);}}catch (IOException e){System.out.println(e);}}
}

属性集

/*java.util.Properties集合 extends Hashtable<k,v> implements Map<k,v>
Properties 类表示一个持久的属性集。Properties 可保存在流中或从流中加载。
Properties 集合是一个唯一和IO流相结合的集合可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用属性列表中每个键及其对应值都是一个字符串Properties集合是一个双列集合,key和value默认都是字符串*/public static void main(String[] args) {/*使用Properties集合存储数据,遍历取出Properties集合中的数据Properties集合是一个双列集合,key和value默认都是字符串Properties集合有一些操作字符串的特有方法Object setProperty(String key, String value) 调用Hashtable的方法put。String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法set<String> stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法*/show01();}public static void show01(){//创建Properties对象Properties prop = new Properties();//使用setProperty方法,往集合中添加数据prop.setProperty("大小姐","167");prop.setProperty("小矮人","168");prop.setProperty("毒蘑菇","169");prop.setProperty("皇后","170");//使用stringPropertyNames方法将键以set集合的方式取出来Set<String> str = prop.stringPropertyNames();for (String s : str){System.out.print(s + " ");
//            prop.getProperty(s);System.out.println(prop.getProperty(s));}}//store方法
public static void show02() throws IOException {/*可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储void store(OutputStream out, String comments)void store(Writer writer, String comments)参数:OutputStream out:字节流输出,不能写中文Writer writer:字符流输出,可以写中文String comments:注释,用来解释说明保存的文件是做什么用的,不能使用中文,会产生乱码,默认是Unicode编码,一般使用""空字符串使用步骤:1.创建Properties对象,添加数据2.创建字节流或者字符流对象,在构造方法中绑定输出目的地3.使用store方法,把集合中的临时数据,持久化写入到硬盘中存储4.释放资源*///创建Properties对象Properties prop = new Properties();//使用setProperty方法,往集合中添加数据prop.setProperty("大小姐","167");prop.setProperty("小矮人","168");prop.setProperty("毒蘑菇","169");//创建字节流或者字符流对象,在构造方法中绑定输出目的地FileWriter fw = new FileWriter("demotwo//prop.txt");//使用store方法,把集合中的临时数据,持久化写入到硬盘中存储prop.store(fw, "sava data");//释放资源fw.close();}public static void show03() throws IOException {//创建Properties对象Properties prop = new Properties();//使用setProperty方法,往集合中添加数据prop.setProperty("大小姐","167");prop.setProperty("小矮人","168");prop.setProperty("毒蘑菇","169");prop.store(new FileOutputStream("demotwo//prop2.txt"),"sava data");}//load方法
public static void show04() throws IOException {/*可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用void loac(InputStream input)void load(Reader reader)参数:InputStream input:字节输入流,不能读取含有中文的键值对Reader Reader:字符输入流,能读取含有中文的键值对使用步骤:1.创建Properties对象2.使用Properties集合对象中的方法load读取保存键值对的文件3.遍历Properties集合注意:1.存储键值对的文件中,键与值默认的连接符号可以使用=,或者空格2.存储键值对的文件中,可以用#进行注释,被注释的键值对不会被读取3.存储键值对的文件中,默认读取时就是字符串形式,所以不用加上引号*///创建Properties对象Properties prop = new Properties();prop.load(new FileReader("demotwo//prop.txt"));Set<String> set = prop.stringPropertyNames();for (String key : set){System.out.print(key + " ");System.out.println(prop.getProperty(key));}}public static void show05() throws IOException {Properties prop = new Properties();prop.load(new FileInputStream("demotwo//prop2.txt"));Set<String> set = prop.stringPropertyNames();for (String key : set){System.out.print(key + " ");System.out.println(prop.getProperty(key));}}

缓冲流

缓冲流,也叫高效流,是对4个基本的FileXxx流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流:BufferedInputStream, BufferedOutStream
  • 字符缓冲流:BufferedReader, BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

字节缓冲输出流BufferedOutputStream

public class Demo01OutputStream {/*java.io.BufferedOutputStream extends OutputStreamBufferedOutputStream:字节缓冲输出流继承自父类的共性成员方法:public void close(): 关闭此输出流并释放与此流相关联的任何系统资源public void flush(): 刷新此输出并强制任何缓冲的字节被写出public void write(byte[] b): 将b.length字节从指定的字节数组写入此输处流public void write(byte[] b,int off,int len): 从指定的字节数组写入len字节,从偏移量off开始输出到此输出流public abstract void write(int b): 将指定的字节输出流构造方法:BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入到指定的 底层输出流。BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层参数:OutputStream:字节输出流我们可以传递FileOouputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率int size:指定缓冲流内部缓冲区的大小,不指定默认使用步骤(重点):1、创建FileOutputStream对象,构造方法中绑定要输出的目的地2、创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率3、使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区4、使用BufferedOutputStream对象中的flush,把内部缓冲区中的数据,刷新到文件中5、释放资源(会先调用flush方法刷新数据,第4部可以省略)*/public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("demotwo//a.txt");BufferedOutputStream bfos = new BufferedOutputStream(fos);bfos.write("崔崔是猪".getBytes());
//        bfos.flush();bfos.close();}
}

字节缓冲输入流BufferedInputStream

public class Demo01InputStream {/*java.io.BufferedInputStream extends InputStreamBufferedInputStream:字节缓冲输入流继承自父类的共性成员方法:int read() 从输入流中读取数据的下一个字节int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中void close() 关闭此输入流并释放与该流关联的所有系统资源。构造方法:BufferedInputStream(InputStream input) 创建一个BufferedInputStream并保存其参数,即输入流in,以便将来使用。BufferedInputStream(InputStream input, int size) 创建具有指定缓冲区大小的BufferedInputStream并保存其参数,即输入流参数:InputStream:字节输入流我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率int size:指定缓冲流内部缓冲区的大小,不指定默认使用步骤(重点):1、创建FileInputStream对象,构造方法中绑定要输出的目的地2、创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象效率3、使用BufferedInputStream对象中的方法read,读取文件4、释放资源*/public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("demotwo//a.txt");BufferedInputStream bfis = new BufferedInputStream(fis);byte[] bytes = new byte[1024];int len = 0;while ((len = bfis.read(bytes)) != -1){System.out.println(new String(bytes, 0 ,len));}/*int len = 0;while (len != -1){len = bfis.read();System.out.println();}*/bfis.close();}
}

字符缓冲输入流

public class Demo02BufferedWrite {/*java.io.BufferedWriter extends WriterBufferedWriter:字符缓冲输出流继承自父类的共性成员方法:- void write(int c) 写入单个字符- void writer(char[] cbuf) 写入字符数组- abstract void write(char[] cbuf, int off , int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数- void write(String str) 写入字符串- void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数- void flush() 刷新该流的缓冲- void close() 关闭此流,但要先刷新它构造方法:BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲字符输出流BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流参数:Writer out:字符输出流我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率int sz:指定缓冲区的大小,不写默认大小特有成员方法:void newLine() 写入一个行分隔符,会根据不同的操作系统,获取不同的行分隔符换行:换行符号使用步骤:1、创建字符缓冲输出流对象,构造方法中传递字符输出流2、调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中3、调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中4、释放资源*/public static void main(String[] args) throws IOException {BufferedWriter bw = new BufferedWriter(new FileWriter("demotwo//a.txt"));for (int i = 0; i < 10; i++) {bw.write("崔崔是猪");bw.newLine();}bw.close();}
}

字符缓冲输出流

public class Demo02BufferedReader {/*这里痛字符输入流一样,所以不做过多赘述*/public static void main(String[] args) throws IOException {BufferedReader br = new BufferedReader(new FileReader("demotwo//a.txt"));
//        byte[] bytes = new byte[1024];String line = null;while ((line = br.readLine()) != null){System.out.println(line);}br.close();}
}

转换流

字符编码

编码:字符(能看懂的)-> 字节(看不懂的,比如0101…)

解码:字节(看不懂的,比如0101…)-> 字符(能看懂的)

转换流原理

对象的序列化和反序列化

  • 对象的序列化
    把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化
  • 对象的反序列化
    把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的反序列化

transient关键字

static关键字:静态关键字静态优先于非静态加载到内存中(静态优先于对象进入到内存中)被static修饰的成员变量不能被序列化,序列化的都是对象transient关键字:瞬态关键字被transient修饰成员变量,不能被序列化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XAZcYoad-1636071520939)(C:\Users\13488\AppData\Roaming\Typora\typora-user-images\image-20210925141539722.png)]

InvalidClassException异常

​ 当JVM反序列化对象时,能找到class文件,但是当class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出InvalidClassException异常。

网络编程入门

端口号

端口号是一个逻辑端口,我们无法直接看到端口号,但是可以通过一些软件或者命令进行查看
当我们使用网络软件时,操作系统就会为网络软件随机分配一个端口号
或者网络软件在打开时和系统要指定的端口号端口号是由两个字节组成,取值范围是在0~65535之间
注意:1024之前的端口号不能使用,已经被系统分配给已知的网络软件网络软件的端口号不能重复我们使用ip地址加上端口号,就可以保证数据准确无误的发送到对方计算机的指定软件上常用的端口号:1、80端口2、数据库端口{  MySQL:3306   Oracle:1521}3、Tomcat服务器端口:8080

Tcp通信

通信的步骤:服务器端先启动服务器端不会主动的请求客户端必须使用客户端请求服务器端客户端和服务器端就会建立一个逻辑链接而这个连接中包含一个对象这个对象就是IO对象客户端和服务器端就可以使用IO对象进行通信通信的数据不仅仅是字符所以IO对象是字节流对象
服务器端进行通信时必须明确的两件事情:1、多个客户端同时和服务器进行交互,服务器必须明确和哪个客户端进行的交互在服务器端有一个方法,叫accept客户端获取到请求的客户端对象(Socket s1 = server.accept();)2、多个客户端同时和服务器进行交互,就需要使用多个IO流对象服务器是没有IO流的,服务器可以获取到请求的客户端对象Socket,使用每个客户端Socket中提供的IO流和客户端进行交互(服务器端使用客户端的字节输入流读取客户端发送的数据,服务器端使用客户端的字节输出流给客户端回写数据)

Socket类

package com.hrg.demo.day12net;import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;/*Tcp通信的客户端:向服务器端发送连接请求,给服务器发送数据,读取服务器回写的数据表示客户端的类:java.net.Socket:此类实现客户端套接字。套接字是两台机器间通信的端点。套接字:包含了IP地址和端口号的网络单位构造方法:API文档中查询Socket​(String host, int port)   Creates a stream socket and connects it to the specified port number on the named host.成员方法:outputStream getOutputStream() 返回此套接字的输出流InputStream getInputStream()  返回此套接字的输入流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次握手建立连接通路(这时服务器没有启动,那么就会抛出异常;如果服务器已经启动,那么就可以进行交互了)
*/
public class Demo01TcpClient {public static void main(String[] args) throws IOException {Socket socket = new Socket("127.0.0.1", 8888);OutputStream os = socket.getOutputStream();os.write("你好服务器".getBytes());InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = is.read(bytes);System.out.println(new java.lang.String(bytes, 0, len));socket.close();}
}
package com.hrg.demo.day12net;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,获取到请求的客户端对象Socket3、使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象4、使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据5、使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream6、使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据7、释放资源(Socket,ServerSocket)
*/
public class Demo02TcpServer {public static void main(String[] args) throws IOException {ServerSocket soc = new ServerSocket(8888);Socket socket = soc.accept();InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = is.read(bytes);System.out.println(new java.lang.String(bytes, 0, len));OutputStream os = socket.getOutputStream();os.write("收到谢谢".getBytes());socket.close();soc.close();}
}
文件上传
Tcp通信的文件上传案例
原理:客户端读取本地的文件,把文件上传到服务器,服务器在把上传的文件保存到服务器的硬盘上步骤:
1、客户端使用本地的字节输入流,读取要上传的文件
2、客户端使用网络字节输出流,把读取到的文件上传到服务器
3、服务器使用网络字节输入流,读取客户端上传的文件
4、服务器使用本地字节输出流,把读取到的文件,保存到服务器的硬盘上
5、服务器使用网络字节输出流,给客户端回写一个“上传成功”
6、客户端使用网络字节输入流,读取服务器回写的数据
7、释放资源注意 :客户端和服务器与本地硬盘进行读写,需要使用自己创建的字节流对象(本地流)客户端和服务端之间进行读写,必须使用Socket中提供的字节流对象(网络流)
package com.hrg.demo.day12net.Fileupload;import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;/*文件上传案例的客户端
*/
public class TcpClient {public static void main(String[] args) throws IOException {//1、创建一个FileInputStream对象,构造方法中绑定要读取的数据源FileInputStream fis = new FileInputStream("D:\\photo\\msh.jpg");//2、创建一个Socket客户端对象,并绑定对应的服务器以及端口号Socket socket = new Socket("127.0.0.1", 8888);//3、使用socket对象中的getOutputStream方法,获取网络字节输出流OutputStream os = socket.getOutputStream();//4、使用FileInputStream中的read方法读取本地的文件,同时利用OutputStream中的write向服务器发送数据int len = 0;byte[] bytes = new byte[1024];//这里需要注意,read方法当没有东西读入的时候,不会读入-1,而是进入阻塞while((len = fis.read(bytes)) != -1){os.write(bytes, 0, len);}//这里需要使用shutdownOutput方法,用来中断客户端的输入流,防止客户端与服务器端陷入阻塞状态socket.shutdownOutput();//5、读取从服务器端回写的数据InputStream is = socket.getInputStream();while((len = is.read(bytes)) != -1){System.out.println(new String(bytes, 0, len));}//释放资源fis.close();socket.close();}}
package com.hrg.demo.day12net.Fileupload;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;/*文件上传案例的服务器端
*/
public class TcpServer {public static void main(String[] args) throws IOException {//1、首先创建一个ServerSocket服务器对象,获取对应的客户端建立连接ServerSocket serverSocket = new ServerSocket(8888);//2、通过ServerSocket方法的accept()方法,获取对应客户端Socket socket = serverSocket.accept();//3、通过获取到的socket对象,通过网络字节输入流读取数据InputStream is = socket.getInputStream();FileOutputStream fos = new FileOutputStream("D:\\photo\\otherphoto\\first.jpg");byte[] bytes = new byte[1024];int len = 0;while ((len = is.read(bytes)) != -1){fos.write(bytes, 0, len);}//4、读取完成之后,通过Socket对象的getOutputStream方法给客户端回写数据OutputStream os = socket.getOutputStream();os.write("文件上传成功".getBytes());//5、释放资源fos.close();socket.close();serverSocket.close();}
}
//B/S模式//以下是浏览器和服务器进行传递必须要写的三行
//写入HTTP协议响应头,固定写法
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
//必须要写入空行,否则浏览器不解析
out.write("\r\n".getBytes());

一个简单的B/案例:

通过浏览器访问项目下面的index.html页面

package com.hrg.demo.day12net.Tcp;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class Demo03TcpForBS {public static void main(String[] args) throws IOException {//1、创建serverSocket对象ServerSocket serverSocket = new ServerSocket(8080);while (true){//通过ServerSocket对象中accept方法获取socket对象Socket socket = serverSocket.accept();new Thread(new Runnable() {@Overridepublic void run() {try{//通过socket对象得到网络输入流,读取浏览器端传输的数据InputStream is = socket.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is));String str = br.readLine();String[] arr = str.split(" ");String htmlpath = arr[1].substring(1);System.out.println(htmlpath);//创建一个FileInputStream本地输入流,构造方法中绑定要读取的html路径FileInputStream fis = new FileInputStream(htmlpath);//使用socket中getOutputStream方法获取网络字节输出流OutputStream out = socket.getOutputStream();//写入HTTP协议响应头,固定写法out.write("HTTP/1.1 200 OK\r\n".getBytes());out.write("Content-Type:text/html\r\n".getBytes());//必须要写入空行,否则浏览器不解析out.write("\r\n".getBytes());//一读一写复制文件,把服务器读取的文件回写到客户端int len = 0;byte[] bytes = new byte[1024];while ((len = fis.read(bytes)) != -1){out.write(bytes, 0, len);}//1、释放资源fis.close();socket.close();}catch (Exception e){e.printStackTrace();}}}).start();}
//        serverSocket.close();}
}

函数式接口

概念

​ 函数式接口在java中是指:有且仅有一个抽象方法的接口

​ 函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

@FunctionalInterface
public interface MyFunctionalInterface {public abstract void method();}/*函数式接口的使用:一般可以作为方法的参数和返回值
*/
public class Demo01test {//定义一个方法,参数使用函数式接口MyFunctionInterfacepublic static void show(MyFunctionalInterface myInter){myInter.method();}public static void main(String[] args) {//调用show方法,方法的参数是一个接口,所以可以传递接口的实现类show(new MyFunctionalInterfaceImpl());//调用show方法,方法的参数是一个接口,所以可以传递接口的匿名内部类show(new MyFunctionalInterface() {@Overridepublic void method() {System.out.println("使用匿名内部类重写接口中的方法");}});//调用show方法,方法的参数是一个函数式接口,所以可以传递lambda表达式show(() -> {System.out.println("使用lambda表达式");});//简化lambda表达式show(() -> System.out.println("使用简化后的lambda表达式"));}
}

函数式编程

函数式编程是种编程方式,它将电脑运算视为函数的计算。(Lambda表达式就属于函数式编程)

package com.hrg.demo.day13FunctionInterface.com.hrg.Lambda;/*使用lambda优化日志案例lambda的特点:延迟加载(减少性能浪费)Lambda的使用前提,必须存在函数式接口
*/
public class Demo02Lambda {public static void showLog(int level, MessageBuilder mb){if (level == 1){System.out.println(mb.builderMessage());}}public static void main(String[] args) {//定义三个日志信息String msg1 = "Hello";String msg2 = "World";String msg3 = "Java";//调用showLog方法,参数MessageBuilder是一个函数式接口,所以可以传递Lambda表达式showLog(1, () -> {return msg1 + msg2 + msg3;});/*使用lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中只有满足条件,日志的等级是1才会调用接口MessageBuilder中的方法*/}
}

使用Lambda表达式作为参数和返回值

//用函数式接口作为参数传递
/*例如java.lang.Runnable接口就是一个函数式接口,假设有一个startThread方法使用该接口作为参数,那么就可以使用Lambda进行传参。这种情况其实和Thread类的构造方法参数为Runnable没有本质区别
*/
public class Demo01Runnable {//定义一个方法StartThread,方法的参数使用Runnable进行传参public static void startThread(Runnable run){//开启一个线程new Thread(run).start();}public static void main(String[] args) {//调用startThread方法,方法的参数是一个接口,那么我们可以传递这个接口的匿名内部类startThread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了");}});//调用startThread方法,方法的参数是一个函数式接口,所以传递lambda表达式startThread(()->{System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了");});//优化lambda表达式startThread(() -> System.out.println(Thread.currentThread().getName() + "-->" + "线程启动了"));}
}

类似得,如果用一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。

package com.hrg.demo.day13FunctionInterface.com.hrg.LambdaTest;import java.util.Arrays;
import java.util.Comparator;public class Demo02Comparator {public static Comparator<String> getComparator(){//方法的返回值是接口,所以可以返回一个匿名内部类/*return new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return  o1.length() - o2.length();}};*///由于方法的返回值是函数式接口,可以返回lambda表达式/*return (String o1, String o2)->{return  o1.length() - o2.length();};*///同时,我们可以进一步简化lambda表达式return (o1, o2) -> o1.length() - o2.length();}public static void main(String[] args) {String[] arr = {"aaaa","bb","ccccc","dddddddd"};System.out.println(Arrays.toString(arr));//调用Arrays中sort方法,对字符串数组进行排序Arrays.sort(arr, getComparator());//输出排序后的结果System.out.println(Arrays.toString(arr));}
}

常用函数式接口

Supplier接口

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

package com.hrg.demo.day13FunctionInterface.com.hrg.Supplier;import java.util.Arrays;
import java.util.function.Supplier;/*求数组元素的最大值
*/
public class Demo02Test {public static int getMax(Supplier<Integer> sup){return sup.get();}public static void main(String[] args) {int[] arr = {1, 3, 5, 12, 41, 36, 77, 99};int max = getMax(() -> {int temp = arr[0];for (int i : arr) {if (temp < i) {temp = i;}}return temp;});System.out.println(max);}
}

Consumer接口

​ java.util.function.Consumer接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

package com.hrg.demo.day13FunctionInterface.com.hrg.Comsumer;import java.util.function.Consumer;public class Demo01Consumer {/*定义一个方法,方法的参数传递一个字符串,方法的参数传递Consumer接口,泛型使用String,可以使用Consumer接口消费字符串*/public static void method(String name, Consumer<String> con){con.accept(name);}public static void main(String[] args) {//定义一个字符串String name = "崔馨月";//调用method方法
//        method(name, (str) -> System.out.println(str + "是猪"));method(name, (str) -> {String restr = new StringBuffer(str).reverse().toString();System.out.println(restr);});}
}
默认方法:andThen

​ 如果一个方法的参数和返回值全都是Consumer类型,那么就可以实现鲜果:消费数据的时候,实现组合。这个方法就是Consumer接口中的default方法andThen。

package com.hrg.demo.day13FunctionInterface.com.hrg.Comsumer;import java.util.function.Consumer;public class Demo02andThen {//使用一下Consumer的默认方法andThen方法/*Consumer<String> con1Consumer<String> con2String s = "hello"con1.accept(s)con2.accept(s)连接两个接口 再进行消费con1.andThen(con2).accept(s)    这里注意,谁写前面,谁先对s进行消费*/public static void method(String name, Consumer<String> con1, Consumer<String> con2){//        con1.accept(name);
//        con2.accept(name);/*使用andThen方法,把两个连接在一起,然后一起使用这种写法与上面的写法所达到的效果一样*/con1.andThen(con2).accept(name);}public static void main(String[] args) {method("I am CC's father", (str)->{System.out.println(str.toUpperCase());}, (str)->{System.out.println(str + ",i love she.");});}
}

Predicate接口

​ 有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果,这时可以使用java.util.function.Predicate接口

package com.hrg.demo.day13FunctionInterface.com.hrg.Predicate;import java.util.function.Predicate;/*Predicate接口中包含一个抽象方法:boolean test(T t)。用于条件判断的场景:
*/
public class Demo01PredicateTest {public static void method(String name, Predicate<String> pre){System.out.println(pre.test(name));}public static void main(String[] args) {method("hello world", (str)->{return str.length() > 5;});}
}
默认方法:and
package com.hrg.demo.day13FunctionInterface.com.hrg.Predicate;import java.util.function.Predicate;/*利用and方法可以判断当多个predicate都为true时,结果才返回true
*/
public class Demo02Predicate_and {public static boolean getBoolean(String s, Predicate<String> pre1, Predicate<String> pre2){return pre1.and(pre2).test(s);}public static void main(String[] args) {boolean bb = getBoolean("cc is a pig", (str) -> {return str.length() > 5;}, (str) -> {return str.contains("a");});System.out.println(bb);}
}
默认方法:negate
package com.hrg.demo.day13FunctionInterface.com.hrg.Predicate;import java.util.function.Predicate;//negate:将Predicate所得结果取反
public class Demo03Predicate_negate {public static void method(String s, Predicate<String> pre){System.out.println(pre.negate().test(s));}public static void main(String[] args) {method("abc", (str) -> {return str.length() > 5;});}
}
默认方法:or
package com.hrg.demo.day13FunctionInterface.com.hrg.Predicate;import java.util.function.Predicate;//or:即当有多个Predicate接口预测结果时,当有一个为真即为真
public class Demo04Predicate_or {public static void method(String s, Predicate<String> pre1, Predicate<String> pre2){System.out.println(pre1.or(pre2).test(s));}public static void main(String[] args) {method("cuicui",(s)->{return s.length() > 5;},(s)->{return s.contains("a");});}
}
Predicate练习
package com.hrg.demo.day13FunctionInterface.com.hrg.Predicate;import java.util.ArrayList;
import java.util.function.Predicate;/*集合信息的筛选:数组当中由多名成员信息,通过predicate接口将符合条件的的信息存储到ArrayList中
*/
public class Demo05PredicateText {public static ArrayList<String> method(String[] ss, Predicate<String> pre){ArrayList<String> arrayList = new ArrayList<>();for (String s : ss) {if (pre.test(s)){arrayList.add(s);}}return arrayList;}public static void main(String[] args) {String[] arr = {"迪丽热巴,19", "马儿扎哈,21", "崔馨月,18"};ArrayList<String> list = method(arr,(s)->{return Integer.parseInt(s.split(",")[1]) < 20;});for (String temp : list) {System.out.println("姓名:" + temp.split(",")[0] + ",年龄:" + temp.split(",")[1]);}}}

Function接口

​ java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。

/*抽象方法:applyFunction接口中最主要的抽象方法:R apply(T t),根据类型T的参数获取类型R的结果
*/
package com.hrg.demo.day13FunctionInterface.com.hrg.Function;import java.util.function.Function;public class Demo01FunctionApply {private static void method(Function<String, Integer> function){int num = function.apply("10");System.out.println(num + 20);}public static void main(String[] args) {method((str)->{return Integer.parseInt(str);});}
}
默认方法:andThen
/*Function接口中的默认方法andThen:用来进行组合操作需求:把String类型的"123",转换为Integer类型,把转换后的结果加10把增加之后的Integer类型的数据,转换为String类型
*/
package com.hrg.demo.day13FunctionInterface.com.hrg.Function;import java.util.function.Function;public class Demo02FunctionAndThen {public static void change(String s, Function<String, Integer> function1, Function<Integer, String> function2){System.out.println(function1.andThen(function2).apply(s));
//        Integer num = function1.apply(s);
//        System.out.println(function2.apply(num));}public static void main(String[] args) {String s = "123";change(s, (ss)->{return Integer.parseInt(ss) + 10;},(ss)->{return Integer.toString(ss);});}
}

训练:

/*String str = "赵丽颖,20"1、将字符串截取数字年龄部分,得到字符串;2、将上一步的字符串转换成为int类型的数字;3、将上一步的int数字累加100,得到结果int数字。
*/

Steam流

​ 在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

package com.hrg.demo.day14Stream.my.Stream;import java.util.ArrayList;
import java.util.List;/*使用Stream流的方式,遍历集合,对集合中的数据进行过滤Stream流时JDK1.8之后出现的关注的是做什么,而不是怎么做
*/
public class Demo02StreamFilter {public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("张无忌");list.add("张三丰");list.add("张九龄");list.add("赵敏");list.add("周芷若");list.add("不周山");list.add("张六");//对集合中的元素按条件进行过滤list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));}}

​ 这张图展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。

​ 这里的filter、map、skip都是在对函数模型进行操作,集合元素并没有真正被处理。只有当终结方法count执行的时候,整个模型才会按照指定策略进行操作。而这得益于Lambda的延迟执行特性。

​ Stream(流)是一个来自数据源的元素队列

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 :流的来源。可以是集合,数组等。

​ 和以前的Collection操作不同,Stream操作还有两个基础的特征:

  • Pipelining:中间操作都会返回。
  • 内部迭代:以前对集合遍历都是通过iterator或者增强for的方式,显式的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法。

获取流

获取一个流非常简单,有以下几种常用的方法:

  • 所有的Collection集合都可以通过stream默认方法获取流;
  • Stream接口的静态方法of可以获取数组对应的流。
package com.hrg.demo.day14Stream.my.Stream;import java.util.*;
import java.util.stream.Stream;public class Demo03GetStream {/*获取一个流的方法:*/public static void main(String[] args) {//**第一种:创建一个集合,把集合转换为Stream流List<String> list = new ArrayList<>();Stream<String> stream01 = list.stream();Set<String> set = new HashSet<>();Stream<String> stream02 = set.stream();Map<String,String> map = new HashMap<>();//获取键,存储到一个set集合种Set<String> keySet = map.keySet();Stream<String> stream03 = keySet.stream();//或者获取值Collection<String> values = map.values();Stream<String> stream04 = values.stream();//或者获取键值对(键与值的映射关系 entrySet)Set<Map.Entry<String, String>> entries = map.entrySet();Stream<Map.Entry<String, String>> stream05 = entries.stream();//**第二种:把数组转换为Stream流Stream<Integer> stream06 = Stream.of(1, 2, 3, 4, 5, 6);//可变参数可以传递数组Integer[] arr = {1, 2, 3, 4, 5};Stream<Integer> stream07 = Stream.of(arr);}
}

常用方法

  • 延迟方法:返回值类型仍然是Stream接口自身的类型的方法,因此可以支持链式调用。
  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuiler那样的链式调用。终结方法包括count和foreach方法。
forEach方法的使用
package com.hrg.demo.day14Stream.my.Stream;import java.util.stream.Stream;/*Stream流中的常用方法_forEachvoid forEach(Consumer<? super T> action);该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理Consumer接口是一个消费型的函数式接口,可以传递lambda表达式,消费数据简单记:forEach方法,用来遍历流中的数据是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
*/
public class Demo04Stream_forEach {public static void main(String[] args) {//获取一个流Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六", "田七");//使用Stream流中的方法forEach对流中的数据进行遍历stream.forEach((s)->{System.out.println(s);});}
}
filter方法的使用
package com.hrg.demo.day14Stream.my.Stream;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;/*Stream流中的常用方法_filter:用于对Stream流中的数据进行过滤Stream<T> filter<Predicate<? super T> predicate);filter方法的参数predicate是一个函数式接口,可以传递Lambda表达式,对数据进行过滤Predicate中的抽象方法:boolean Test(T t);
*/
public class Demo05Stream_Filter {public static void main(String[] args) {//创建一个Stream流String[] str = {"张一山,22","刘德华,33","古天乐,44","胡歌,39","周星驰,60"};Stream<String> stream = Stream.of(str);//这里不用传入一个字符串数组,stream.filter会自动对字符串数组中每一个字符串进行判断Stream<String> stream2 = stream.filter((String ss) -> {String[] strings = ss.split(",");if (Integer.parseInt(strings[1]) >= 40) {return true;}return false;});stream2.forEach(s -> System.out.println(s));}
}
映射:map方法

如果需要将流中的元素映射到另一个流中,可以使用map方法。

package com.hrg.demo.day14Stream.my.Stream;import java.util.stream.Stream;/*如果需要将流中的元素映射到另一个流中,可以使用map方法。<R> Stream<R> map(Function<? super T,? extends R> mapper);该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。Function中的抽象方法:R apply(T t);
*/
public class Demo06Stream_Map {public static void main(String[] args) {//获取一个String类型的Stream流Stream<String> stream = Stream.of("1", "2", "3", "4", "5");//使用map方法,把字符串类型的整数,转换(映射)为Integer类型的整数Stream<Integer> stream1 = stream.map((String s) -> {return Integer.parseInt(s);});stream1.forEach(s -> System.out.println(s));}
}
Count方法
package com.hrg.demo.day14Stream.my.Stream;import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;/*Stream流中的常用方法_count:用于统计Stream流中元素的个数Long count();count方法是一个终结方法,返回值是一个long类型的整数所以不能再继续调用Stream流中的其他方法了
*/
public class Demo07Stream_Count {public static void main(String[] args) {//获取一个Stream流List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);list.add(5);Stream<Integer> stream = list.stream();//计算流中元素个数System.out.println(stream.count());}
}
limit方法
package com.hrg.demo.day14Stream.my.Stream;import java.util.stream.Stream;/*Stream流中常用的方法_limit:用于截取流中的元素limit方法可以对流进行截取,只取用前n个。Stream<T> limit(long maxSize);参数是一个long类型,如果集合当前长度大于参数则进行截取;否则不进行操作limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,可以继续调用Stream流中的其他方法
*/
public class Demo08Stream_Limit {public static void main(String[] args) {//获取一个Stream流String[] arr = {"美羊羊","喜羊羊","懒洋洋","沸羊羊","灰太狼","红太狼"};Stream<String> stream = Stream.of(arr);//使用limit对Stream流中的元素进行截取,只要前4个元素Stream<String> stream2 = stream.limit(4);//遍历流stream2.forEach(s -> System.out.println(s));}
}
Skip方法
package com.hrg.demo.day14Stream.my.Stream;import java.util.stream.Stream;/*Stream流中常用的方法_skip:用于跳过元素Stream<T> skip(long n);如果流的当前长度大于n,则跳过前n个;否则将会得到 一个长度为0的空流;
*/
public class Demo09Stream_Skip {public static void main(String[] args) {//获取一个Stream流String[] arr = {"美羊羊","喜羊羊","懒洋洋","沸羊羊","灰太狼","红太狼"};Stream<String> stream = Stream.of(arr);//跳过前4个元素Stream<String> stream1 = stream.skip(4);//遍历流stream1.forEach(s -> System.out.println(s));}
}
组合:concat
package com.hrg.demo.day14Stream.my.Stream;import java.util.stream.Stream;/*Stream流中常用的方法_concat:将两个流合并称为一个流(Stream流中的静态方法)Stream<T> concat(Stream<? extends T> a,Stream<? extends T> b);*/
public class Demo10Stream_Concat {public static void main(String[] args) {//获取一个Stream流String[] arr = {"美羊羊","喜羊羊","懒洋洋","沸羊羊","灰太狼","红太狼"};Stream<String> streamA = Stream.of(arr);//获取一个String类型的Stream流Stream<String> streamB = Stream.of("1", "2", "3", "4", "5");Stream<String> concatStream = Stream.concat(streamA, streamB);concatStream.forEach(s-> System.out.println(s));}
}

方法引用

方法引用符

​ 双冒号**’::’**为引用运算符,而他所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么可以通过双冒号来引用该方法作为Lambda的替代者。

package com.hrg.demo.day14Stream.my.reference;/*定义一个打印的函数式接口
*/
@FunctionalInterface
public interface Printable {public abstract void print(String s);
}package com.hrg.demo.day14Stream.my.reference;public class Demo01Printable {public static void printString(Printable p){p.print("hello world!");}public static void main(String[] args) {//调用该方法printString(s -> System.out.println(s));/*分析:Lambda表达式的目的,打印参数传递的字符串把参数s,传递给了System.out对象,调用out对象中的方法println对字符串进行了输出注意:1、System.out对象是已经存在2、println方法也是已经存在所以我们可以使用方法引用来优化lambda表达式可以使用System.out方法直接引用println方法*/printString(System.out::println);}
}

通过对象名引用成员方法

package com.hrg.demo.day14Stream.my.reference;public class MethodRerObject {//定义一个成员方法,传递字符串,把字符串按照大写输出public void printUpperCase(String s){System.out.println(s.toUpperCase());}
}package com.hrg.demo.day14Stream.my.reference;import java.lang.invoke.MethodHandle;/*通过对象名引用成员方法使用前提式对象名是已经存在的,成员方法也是已经存在的就可以使用对象名引用成员方法
*/
public class Demo02ObjectMethodRer {//定义一个方法,方法的参数传递Printable接口public static void printStringAll(Printable p){p.print("Hello");}public static void main(String[] args) {//调用PrintString方法printStringAll(s -> System.out.println(s.toUpperCase()));//进行方法引用//如果引用成员的是静态方法,可以直接使用类名加成员方法引用
//        printStringAll(MethodRerObject::printUpperCase);/*如果不是静态方法,可以通过创建对象来使用注意:如果是静态方法,这里通过创建对象来使用是不正确的*/MethodRerObject obj = new MethodRerObject();printStringAll(obj::printUpperCase);}
}

通过super引用父类成员方法

package com.hrg.demo.day14Stream.my.reference;public class Man extends Human {//子类重写父类方法public void sayHello(){System.out.println("hello i'm Man.");}public void method(Greetable b){b.Greet();}public void show(){/*method(()->{//创建父类Human h = new Human();//调用父类的sayHello方法h.sayHello();});*///因为子父类关系,所以存在一个关键字super,代表父类,所以我们可以直接调用父类的成员方法method(()->{//            super.sayHello();this.sayHello();});}public static void main(String[] args) {new Man().show();}
}

通过this引用本类成员方法

package com.hrg.demo.day14Stream.my.reference.DemoThis;@FunctionalInterface
public interface Richable {public abstract void buy();
}package com.hrg.demo.day14Stream.my.reference.DemoThis;public class Husband {//定义一个买房子的方法public void buyHouse(){System.out.println("四川买一栋别墅");}//定义一个结婚的方法public void marry(Richable r){r.buy();}//定义一个高兴的方法public void soHappy(){/*marry(()->{this.buyHouse();});*//** 使用方法引用优化Lambda表达式* this是已经存在的* 本类的成员方法buyHouse也是已经存在的* 所以我们可以通过this引用本类方法* */marry(this::buyHouse);}public static void main(String[] args) {new Husband().soHappy();}
}

使用构造方法引用

package com.hrg.demo.day14Stream.my.reference.DemoConstrutor;
/*定义一个创建Person对象的函数式接口
* */
@FunctionalInterface
public interface PersonBuilder {//定义一个方法,根据传递的姓名,创建Person对象并返回public abstract Person builderPerson(String name);
}package com.hrg.demo.day14Stream.my.reference.DemoConstrutor;/*
* 类的构造器(构造方法)引用,Person是一个只有name属性的类
* */
public class Demo {//定义一个方法,参数传递姓名和PersonBuilder接口,方法中通过姓名创建Person对象public static void printName(String name,PersonBuilder pb){Person person = pb.builderPerson(name);System.out.println(person);}public static void main(String[] args) {//调用printName方法,方法的参数PersonBuilder接口是一个函数式接口,可以传递lambdaprintName("ccc",(String name)->{return new Person(name);});//使用方法引用printName("hrg",Person::new);}
}

数组的构造引用

package com.hrg.demo.day14Stream.my.reference.DemoArray;/*
* 数组的构造器引用
* */
public class Demo {/*定义一个方法,方法参数传递创建数组的长度和ArrayBuilder接口方法内部根据传递的长度使用ArrayBuilder中的方法创建数组返回*/public static int[] createArray(int length, ArrayBuilder ab){return ab.builderArray(length);}public static void main(String[] args) {//调用createArray方法,传递数组长度和lambda表达式int[] array1 = createArray(10, (len) -> {return new int[len];});System.out.println(array1.length);//方法引用int[] array2 = createArray(12, int[]::new);System.out.println(array2.length);}
}

整理的java基础知识点笔记相关推荐

  1. java基础知识点整理一

    java基础知识点整理一 引言 '''突然发觉任何一门语言的基础知识部分,都比较杂一些.如果个人经过梳理之后,知识体系系统化,可以让基础更加牢靠一些.但是还是会有一些遗忘.所以,我想把一些比较重要但是 ...

  2. Java基础知识点整理(2022年最新版)

    看了网上很多关于Java基础知识点整理的文章,但是感觉都不是很好,要么不全面,要么不准确,要么排版太乱了,所以今天整理了一份Java基础知识点整理(2022年最新版),希望对大家有帮助哈~ 由于本文篇 ...

  3. 知识点整理,Java基础面试题(一)

    写在前面 整理了一些互联网大厂的面试题,这些面试题经常会被问到,也是作为Java工程师需要掌握的一些知识点,毕竟理论和实践的结合,才是王道,分片整理,每天嗑些知识点,快乐每一天,如果对你有帮助,记得点 ...

  4. 2022疫情缩水,啃透这份399页Java架构知识点笔记,已从13K涨到25K

    本来已经在为去大厂工作摩拳擦掌的Java朋友,社招又是需要5年以上的,今年显得格外艰难: 就业人数高达874万!人才竞争加剧! 疫情让大多数公司的招聘需求缩减!对社招来说,人才招聘要求愈来愈高! 别说 ...

  5. Java基础知识笔记-11_2-Swing用户界面组件

    Java基础知识笔记-11_2-Swing用户界面组件 这章教程两个版本,一个语法是非lambda表达式版本,另一个是lambda表达式版本 非lambda表达式版本 1 Java Swing概述 J ...

  6. 尚学堂JAVA基础学习笔记_2/2

    尚学堂JAVA基础学习笔记_2/2 文章目录 尚学堂JAVA基础学习笔记_2/2 写在前面 第10章 IO技术 1. IO入门 2. IO的API 3. 装饰流 4. IO实战 5. CommonsI ...

  7. 51自学网-Java基础视频教程-笔记 最后修改于2020/9/9

    Java基础视频教程笔记 简介 正文 ==JVM Java Virtual Machine== 栈区 Stack Segment 堆区 Heap Segment 静态区 Data Segment 代码 ...

  8. 中职计算机基础知识点笔记2

    中职计算机基础知识点笔记1 文章目录 1.3数字化信息编码与数据表示 1.3.1进制计数制 1.3.2进制转换 1.3.3原码.反码.补码 1.3.4常用信息编码 1.4计算机安全防护知识 1.4.1 ...

  9. java重要基础知识点_必看 | 新人必看的Java基础知识点大梳理

    原标题:必看 | 新人必看的Java基础知识点大梳理 各位正在认真苦学Java的准大神,在这烈日炎炎的夏季里,老九君准备给大家带来一个超级大的"冰镇西瓜,"给大家清凉一下,压压惊. ...

最新文章

  1. html 生成唯一码,生成唯一邀请码.html
  2. pgrouting进行路径规划之入门二
  3. bigdecimal不保留小数_金钱要使用BigDecimal数据类型(使用double的已经被公司开除了)...
  4. 自定义hashCode()
  5. XMC-GAN:从文本到图像的跨模态对比学习
  6. 图谱实战 | 京东商品图谱构建与实体对齐
  7. 第7章 随机扭动的曲线(《Python趣味创意编程》教学视频)
  8. 汉字区位码查询与算法
  9. 做人10大心机:不能太单纯 适度伪装自己
  10. 靶机渗透练习04-driftingblues4
  11. 【FPGA】Vivado综合停滞、死机(PID Not Specified)解决方法
  12. 【Android】组件安全
  13. 2012,当我们谈论移动互联网创业时,我们在谈论些什么?
  14. Codeforces Round #460 (Div. 2) C Seat Arrangements
  15. centos安装部分开发软件
  16. STM32使用正点原子无线烧录器无线查看数据波形
  17. 添加友情链接获取CF币
  18. jude 5.5.2 UML
  19. Java开发项目常见BUG
  20. 华为鸿蒙和美的,美的与华为鸿蒙合作,为智能家居领域带来更深度的场景与服务...

热门文章

  1. navicat for mysql 12 破解工具 亲测可用
  2. 雷电三接口有什么用_三坐标为什么用汽浮轴承?
  3. win10 64位装三菱PLC软件出现oleaut32.dll拒绝访问
  4. 【升级版】python全自动定时,循环发消息(微信、QQ),零基础应用,
  5. 关于ubuntu 下载后没有网的情况
  6. 重复性、分辨率的计算方法
  7. Android屏幕图片资源大小
  8. PHP设计模式(1)
  9. 用c语言绘制五角星图形,用c语言画出一个五角星图案
  10. 中国公交广告高峰论坛——天津