目录
2.在表达式中,byte,short,char是直接转化成int进行运算
3.大范围的数据不能直接赋值给小范围的变量,但是可以强制转换变量
获取时间(LocalDate,LocalTime,LocalDateTime)
Lambda表达式的省略写法(进一步简化Lambda表达式的写法)
基础知识
命名篇
- 标识符:由数字,字母,下划线和$等组成
- 强制要求:不能以数字开头,关键字不能做名字,且区分大小写的
-
关键字:不能作为类名,变量名,比如:
标识符建议规范:
- 变量名称:同时建议用英文,有意义,首字母小写,eg:
int stduyNumber=10;
- 类名称:建议全英文,有意义,首字母大写,eg:HelloWorld
储存类型
字符存储是同C语言
图片是通过RGB三色数据进行存储
声音是波形图
Java中数据类型基本同C语言,无long long,布尔类型 Boolean 字符串是string
类型转化
1.表达式的类型由最高类型决定
eg:
int a = 10;
byte b = 20;
long c = 300;
long res = a + b + c;
System.out.println(res);
其中res的类型就是long
2.在表达式中,byte,short,char是直接转化成int进行运算
原因:防止数据溢出
修改如下:
byte a = 10;
short b = 10;
int c = a + b;
3.大范围的数据不能直接赋值给小范围的变量,但是可以强制转换变量
强制转换 可能造成数据丢失溢出
浮点型转换成整型,直接丢掉小数部分,保留整数
运算符
或非与
同C语言
逻辑异或
两边结果不同返回true 反之
&& 和 ||
&& (短路与) 从左边开始运行,如果左边false,则右边不运行
||(短路或)从左边开始运行,如果左边true,则右边不运行
由于&&,||运行效率更高,在开发中用的更多
&&的时候,运行结果
&的时候,运行结果
&&优先级高于||
输入操作
步骤 :
导包:
import java.util.Scanner;
得到扫描器对象:
Scanner sc = new Scanner(System.in);
等待接收数据:
int a = sc.nextInt();
随机数
步骤:
//1.导包、
import java.util.Random;
//2.创建一个Random对象,用于生成随机数
Random r = new Random();
//3.调用Random提供的功能:nextInt得到随机数
int data = r.nextInt(100);
多个变量同时指向同一个对象:
数组1的地址被赋给数组2,数组1与数组2的地址是相同的
package com.ljt.study;
import java.util.Scanner;
public class ArrayDemo {
public static void main(String[] args) {
int[] ages = new int[]{12, 23, 45};
int[] ages2 = {12, 23, 34};
int ages3[] = {12, 32, 54};
Scanner sc = new Scanner(System.in);
int arr1[] = new int[4];
int[] arr2 = arr1; //数组1的地址被赋给数组2,数组1与数组2的地址是相同的
for (int i = 0; i < arr1.length; i++) {
arr1[i] = sc.nextInt();
}
arr2[1] = 99;
for (int i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);
}
int arr[] = new int[6];
Scanner sc = new Scanner(System.in);
for (int i = 0; i < arr.length; i++) {
System.out.println("请输入一个数字:");
arr[i] = sc.nextInt();
}
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
方法
( 类似于C语言的函数)
注意Java中没有指针,但是改变数组数据时是对地址中数据改变(类似于指针操作,但仅限于数组中)称之为引用传递
方法重载
一个类中出现多个方法名称都相同的,但是它们的形参列表不同,那称之为方法重载
面对对象基础
(类似与c的结构体,但是具有储存方法,可以直接调用方法)
初始化
public class 类名{
1.变量,用来说明对象可以处理什么数据
2.方法,描述对象有什么功能,可以对数据进行什么样的处理。
...
}
注意事项:
1、类名建议用英文单词,首字母大写,满足驼峰模式,且要有意义,比如:Student、Car..
2、类中定义的变量也称为成员变量(对象的属性),类中定义的方法也称为成员方法(对象的行为)
3、成员变量本身存在默认值,同学们在定义成员变量时一般来说不需要赋初始值(没有意义)。
4、一个代码文件中,可以写多个class类,但只能一个用public修饰,且public修饰的类名必须成为代码文件名。
5、对象与对象之间的数据不会相互影响,但多个变量指向同一个对象时就会相互影响了。
6、如果某个对象没有一个变量引用它,则该对象无法被操作了,该对象会成为所谓的垃圾对象。
This
作用:用来解决对象的成员变量与方法内部变量的名称一样时,导致访问冲突问题的。
this就是一个变量,可以用在方法中,用来拿到当前对象 ; 哪个对象调用方法this就指向哪个对象,也就是拿到哪个对象。
构造器
构造器的常用场景
创建对象时,同时完成对对象成员变量(属性)的初始化赋值。
构造器的特点
创建对象时,又象会去调用构造器。
构造器的注意事项
- 类在设计时,如果不写构造器,Java是会为类自动生成一个无参构造器的,
- 一旦定义了有参数构造器,Java就不会帮我们的类自动生成无参构造器了,此时就建议自己手写一个无参数构造器出
封装
设计规范:合理隐藏,合理暴露
什么是封装?
- 就是用类设计对象处理某一个事物的数据时,应该把要处理的数据,以及处理这些数据的方法设计到一个对象中去。
- 面向对象的三大特征:封装、继承、多态。
代码层面如何控对象的成员公开或隐藏?
- 公开成员,可以使用public(公开)进行修饰
- 隐藏成员,使用private(私有,隐藏)进行修饰
package encapsulation;
public class Student {
private double score;
public void setScore(double score) {
if (score >= 0 && score <= 100) {
this.score = score;
} else {
System.out.println("you put a false numbers");
}
}
public double getScore(){
return score;
}
}
public class Text {
public static void main(String[] args) {
Student s = new Student();
s.setScore(99);
System.out.println(s.getScore());
}
}
正常数据:
异常数据:
电影系统查询
package Demo;
import java.util.Scanner;
public class Text {
public static void main(String[] args) {
Movie[] movies = new Movie[4];
movies[0] = new Movie(1, "水门桥", 38.9, 9.8, "徐克", "吴京", "12万人想看");
movies[1] = new Movie(2, "出拳吧", 39, 7.8, "唐晓", "田雨", "3.5万人想看");
movies[2] = new Movie(3, "月球陨落", 42, 7.9, "罗兰", "贝瑞", "17.9万人想看");
movies[3] = new Movie(4, "一点就到家", 35, 8.7, "许宏宇", "刘吴然", "10.8万人想看");
MovieOperator operator = new MovieOperator(movies);
while (true) {
System.out.println("==电影信息系统==");
System.out.println("1、查询全部电影信息");
System.out.println("2、根据id查询某个电影的详细信息展示");
System.out.println("请您输入操作命令:");
Scanner sc = new Scanner(System.in);
int command = sc.nextInt();
switch (command) {
case 1:
// 展示全部电影信息
operator.printAllMovies();
break;
case 2:
//根据id查询某个电影的详细信息展示
System.out.println("请您输入查询的电影id:");
int id = sc.nextInt();
operator.searchMovieById(id);
break;
default:
System.out.println("您输入的命令有问题~~");
}
}
}
}
public class Movie {
private int id;
private String name;
private double price;
private double score;
private String director;
private String actor;
private String info;
public Movie() {
}
public Movie(int id, String name, double price, double score, String director, String actor, String info) {
this.id = id;
this.name = name;
this.price = price;
this.score = score;
this.director = director;
this.actor = actor;
this.info = info;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public double getScore() {
return score;
}
public String getDirector() {
return director;
}
public String getActor() {
return actor;
}
public String getInfo() {
return info;
}
}
public class MovieOperator {
private Movie[] movies;
public MovieOperator(Movie[] movies) {
this.movies = movies;
}
//把电影信息传入当前方法
public void printAllMovies() {
System.out.println("-------系统全部电影信息如下:---------");
for (int i = 0; i < movies.length; i++) {
Movie m = movies[i];
System.out.println("编号:" + m.getId());
System.out.println("名称:" + m.getName());
System.out.println("价格:" + m.getPrice());
System.out.println("----------------------------");
}
}
public void searchMovieById(int id) {
for (int i = 0; i < movies.length; i++) {
Movie m = movies[i];
if (m.getId() == id) {
System.out.println("该电影详情如下:");
System.out.println("编号:" + m.getId());
System.out.println("名称:" + m.getName());
System.out.println("价格:" + m.getPrice());
System.out.println("得分:" + m.getScore());
System.out.println("导演:" + m.getDirector());
System.out.println("主演:" + m.getActor());
System.out.println("其他信息:" + m.getInfo());
return;//已经找到了电影信息,没有必要再执行了
}
}
System.out.println("没有该电影信息~");
}
}
成员变量与局部变量
API
String
package string;
public class StringDemo1 {
public static void main(String[] args) {
String name = "good";
System.out.println(name);
String rs1 = new String();
System.out.println(rs1);
String rs2 = new String("good");
System.out.println(rs2);
//直接接受字符串
char[] chars = {'a', 'b', 'c'};
String rs3 = new String(chars);
System.out.println(rs3);
//通过ASCII码进行转译
byte[] bytes = {97, 98, 99};
String rs4 = new String(bytes);
System.out.println(rs4);
}
}
常用API
String
package string;
public class StringDemo2 {
public static void main(String[] args) {
String s = "牛波伊Java";
System.out.println(s.length());
for (int i = 0; i < s.length(); i++) {
System.out.println(s.charAt(i));
}
String s1 = new String("der");
String s2 = new String("der");
System.out.println(s1 == s2);//false
//返回false是因为s1 与 s2 地址不同
System.out.println(s1.equals(s2));//true
//对比内容是否相同
String c1 = "34AeFG";
String c2 = "34aEfg";
System.out.println(c1.equals(c2));//false 精准对比
System.out.println(c1.equalsIgnoreCase(c2));//true 忽略大小写
//截取字符串(报前不包后)
String s3 = "java是最好的编程语言之一";
String rs = s3.substring(0, 8);
System.out.println(rs);
//从当前位置一直截取到末尾
String rs2 = s3.substring(5);
System.out.println(rs2);
//把字符串替换成新内容
String info = "垃圾垃圾垃圾";
String rs3 = info.replace("垃圾", "**");
System.out.println(rs3);
//判断字符串是否含某个关键字
String info2 = "Java好好好";
System.out.println(info2.contains("Java"));
System.out.println(info2.contains("java"));
System.out.println(info2.contains("Java2"));
//判断字符串开头内容
String rs4 = "张三丰";
System.out.println(rs4.startsWith("张"));
//把字符串按照某个指定内容分割成多个字符串,放到一个字符串数组中
String rs5 = "张无忌,周芷若,张三丰";
String[] names = rs5.split(",");
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
}
}
运行结果:
ArrayList
public class ArrayDemo1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//1.直接插入数据
list.add("ASD");
list.add("ZXC");
System.out.println(list);
//2.在某个位置插入数据
list.add(1,"der");
System.out.println(list);
//3.根据索引获取集合中某个索引位置处的值
String rs= list.get(1);
System.out.println(rs);
//4.获取集合的大小(返回集合中存储的元素个数)
System.out.println(list.size());
//5.根据索引删除集合中的某个元素值,会返回被删除的元素值给我们
System.out.println(list.remove(1));
System.out.println(list);
//6.直接删除某个元素值,删除成功会返回true,反之
System.out.println(list.remove("ASD"));
System.out.println(list);
// 默认删除的是第一次出现的这个数据的
list.add( 1, "html");
list.add( 0, "html");
System.out.println(list);
System.out.println(list.remove("html") );
System.out.println(list);
// 7、修改某个索引位置处的数据,修改后会返回原来的值给我们
System.out.println(list.set(1,"good"));
System.out.println(list);
}
}
public class ArrayDemo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("枸杞");
list.add("Java入门");
list.add("黑枸杞");
list.add("人字拖");
list.add("特级枸杞");
list.add("枸杞子");
System.out.println(list);
//从前面遍历 需要i--;
for (int i = 0; i < list.size(); i++) {
String res = list.get(i);
if (res.contains("枸杞")) {
list.remove(res);
i--;
}
}
// //从后面遍历
// for (int i = list.size() - 1; i >= 0; i--) {
// String res = list.get(i);
// if (res.contains("枸杞")) {
// list.remove(res);
// }
// }
System.out.println(list);
}
}
public class student {
static String name;
int age;
}
public class Test {
public static void main(String[] args) {
student.name = "ljt";
student s1 = new student();
s1.name = "der";
//因为这个有点类似于全局变量
System.out.println(s1.name); //der
System.out.println(student.name); //der
}
}
Object
常用API
Modifier and Type | 方法 | 描述 |
---|---|---|
protected Object | clone() | 创建并返回此对象的副本。 |
boolean | equals(Object obj) | 指示一些其他对象是否等于此。 |
String | toString() | 返回对象的字符串表示形式 |
equals和toString
import java.util.Objects;
public class text {
public static void main(String[] args) {
Student s1 = new Student("der", 18);
System.out.println(s1);
Student s2 = new Student("der", 18);
System.out.println(s2 == (s1));
System.out.println(s2.equals(s1));
}
}
public class Student {
private String name;
private int age;
public Student() {
}
//String是java提供的,java把String类的equals方法重写了,Object类中的equals方法默认==比较
//比较内容是否一致
//比较者:s2 == this
//被比较者:s1 == o
@Override
public boolean equals(Object o) {
//1.判断地址是否相同
if (this == o) return true;
//2.判断o是否为null直接返回False,或者比较类型是否一致,返回False
if (o == null || getClass() != o.getClass()) return false;
//3,o不是null,且o一定是对应类型的对象,开始比较内容
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
equals 方法注意!!!:String是java提供的,java把String类的equals方法重写了,Object类中的equals方法默认==比较
如果对方法不进行重写的话,会返回false。如图:
//String是java提供的,java把String类的equals方法重写了,Object类中的equals方法默认==比较
//比较内容是否一致
//比较者:s2 == this
//被比较者:s1 == o
@Override
public boolean equals(Object o) {
//1.判断地址是否相同
if (this == o) return true;
//2.判断o是否为null直接返回False,或者比较类型是否一致,返回False
if (o == null || getClass() != o.getClass()) return false;
//3,o不是null,且o一定是对应类型的对象,开始比较内容
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
对方法进行重写后,如图:
clone
分为浅克隆和深克隆
浅克隆:
- 拷贝出的新对象,与原对象中的数据一模一样(引用类型拷贝的是地址)
public class Text {
public static void main(String[] args) throws CloneNotSupportedException {
//若子类与基类(父类)不在同一包中,那么在子类中,只有子类实例可以访问其从基类继承而来的protected方法,
// 而在子类中不能访问基类实例(对象)(所调用)的protected方法。
User u1 = new User(2, "der666", "der", new int[]{1, 2, 3, 4});
User u2 = (User) u1.clone();
System.out.println(u1.getId());
System.out.println(u1.getName());
System.out.println(u1.getScore());
System.out.println(u1.getPassWord());
System.out.println("---------------------------------");
System.out.println(u2.getId());
System.out.println(u2.getName());
System.out.println(u2.getScore());
System.out.println(u2.getPassWord());
}
}
public class User implements Cloneable{
private int id;
private String PassWord;
private String name;
private int[] Score;
public User(int id, String passWord, String name, int[] score) {
this.id = id;
PassWord = passWord;
this.name = name;
Score = score;
}
public int[] getScore() {
return Score;
}
public void setScore(int[] score) {
Score = score;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//通过super调用父类的
return super.clone();
}
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPassWord() {
return PassWord;
}
public void setPassWord(String passWord) {
PassWord = passWord;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
深克隆:
- 对象中基本类型的数据直接拷贝。
- 对象中的字符串数据拷贝的还是地址。
- 对象中还包含的其他对象,不会拷贝地址,会创建新对象。
- 核心变化:
@Override protected Object clone() throws CloneNotSupportedException { //通过super调用父类的 User u = (User) super.clone(); u.Score = u.Score.clone(); return u; }
public class Text {
public static void main(String[] args) throws CloneNotSupportedException {
//若子类与基类(父类)不在同一包中,那么在子类中,只有子类实例可以访问其从基类继承而来的protected方法,
// 而在子类中不能访问基类实例(对象)(所调用)的protected方法。
User u1 = new User(2, "der666", "der", new int[]{1, 2, 3, 4});
User u2 = (User) u1.clone();
System.out.println(u1.getId());
System.out.println(u1.getName());
System.out.println(u1.getScore());
System.out.println(u1.getPassWord());
System.out.println("---------------------------------");
System.out.println(u2.getId());
System.out.println(u2.getName());
System.out.println(u2.getScore());
System.out.println(u2.getPassWord());
}
}
public class User implements Cloneable{
private int id;
private String PassWord;
private String name;
private int[] Score;
public User(int id, String passWord, String name, int[] score) {
this.id = id;
PassWord = passWord;
this.name = name;
Score = score;
}
public int[] getScore() {
return Score;
}
public void setScore(int[] score) {
Score = score;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//通过super调用父类的
User u = (User) super.clone();
u.Score = u.Score.clone();
return u;
}
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getPassWord() {
return PassWord;
}
public void setPassWord(String passWord) {
PassWord = passWord;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Objects
Modifier and Type | 方法 | 描述 |
---|---|---|
static boolean | isNull(Object obj) | 返回 |
static boolean | nonNull(Object obj) | 返回 |
static boolean | equals(Object a, Object b) | 返回 |
equals
直接通过调用equals,可能会出现空指针异常!会报错的
错误代码!!(不要Ctrl+V错了哟)
System.out.println(s1.equals(s2));
改进方式:
正确代码(Ctrl+V这个哟!!!)
System.out.println(Objects.equals(s1,s2));//更加安全,
(探究一下为什么这个就事可以的)
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
这个是Objects的方法(可以通过Ctrl点击相应方法进行观看源码),他是先判断是否为空,如果是空的话直接返回false!!
(附一下源码叭)
import java.util.Objects;
public class Text {
public static void main(String[] args) {
String s1 = null;
String s2 = "der";
// System.out.println(s1.equals(s2));
System.out.println(Objects.equals(s1,s2));//更加安全,
}
}
isNull
方法源码:
public static boolean isNull(Object obj) {
return obj == null;
}
本质上与直接写s1 == null没有区别,(即以下两种写法一样)(用这个显得肥肠高级呢!!)
System.out.println(Objects.isNull(s1));
System.out.println(s1 == null);
import java.util.Objects;
public class Text {
public static void main(String[] args) {
String s1 = null;
String s2 = "der";
System.out.println(Objects.isNull(s1));
System.out.println(s1 == null);
System.out.println(Objects.isNull(s2));
System.out.println(s2 == null);
}
}
nonnull
与isnull正好相反
(话不多说,直接上源码)
public class Text {
public static void main(String[] args) {
String s1 = null;
String s2 = "der";
System.out.println(Objects.nonNull(s1));
System.out.println(Objects.nonNull(s2));
}
}
包装类
包装类就是把基本数据包装成对象。
泛型不支持基本类型数据,只能支持引用类型数据
基本数据类型 | 对应的包装类(引用数据类型) |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
Integer
构造方法:
自动装箱,可以自动把基本数据类型的对象转化为对象
Integer a1 = 1;
自动拆箱,可以自动把包装类型的对象换成相应的基本数据类型
int a2 = a1;
类型转换
1、把基本数据类型的数据转换成字符串
Integer n = 23;
String rs1 = Integer.toString(n);
System.out.println(rs1 + 1);
String rs2 = n.toString();
System.out.println(rs2 + 1);
String rs3 = n + "";
System.out.println(rs3 + 1);
2、把字符串的数值转换成相应的基本类型数据
注意注意!!!!:不可以带有其他字符否则直接报错
一般直接用 要转的类型.valueOf
String agestr = "29";
// int ageI = Integer.parseInt(agestr);
int ageI = Integer.valueOf(agestr);
System.out.println(ageI + 1);
String num = "99.5";
// double numD = Double.parseDouble(num);
double numD = Double.valueOf(num);
System.out.println(numD + 0.5);
源码~~~~
import java.util.ArrayList;
public class Text {
public static void main(String[] args) {
//Integer i = new Integer(1);
Integer i = Integer.valueOf(1);
System.out.println(i);
//自动装箱,可以自动把基本数据类型的对象转化为对象
Integer a1 = 1;
//自动拆箱,可以自动把包装类型的对象换成相应的基本数据类型
int a2 = a1;
//泛型不支持基本类型数据,只能支持引用类型数据
//ArrayList<int> list = new ArrayList<int>();
ArrayList<Integer> list = new ArrayList<>();
list.add(12);
list.add(22);
int res = list.get(0);
System.out.println(res);
//1、把基本数据类型的数据转换成字符串
System.out.println("--------------");
Integer n = 23;
String rs1 = Integer.toString(n);
System.out.println(rs1 + 1);
String rs2 = n.toString();
System.out.println(rs2 + 1);
String rs3 = n + "";
System.out.println(rs3 + 1);
//2、把字符串的数值转换成相应的基本类型数据
String agestr = "29";
// int ageI = Integer.parseInt(agestr);
int ageI = Integer.valueOf(agestr);
System.out.println(ageI + 1);
String num = "99.5";
// double numD = Double.parseDouble(num);
double numD = Double.valueOf(num);
System.out.println(numD + 0.5);
}
}
StringBuilder与StringBuffer
StringBuilder
- StringBuilder代表可变字符串对象,相当于是一个容器,它里面装的字符串是可以改变的,就是用来操作字符串的
- 好处:stringBuilder比String更适合做字符串的修改操作,效率会更高,代码也会更简洁
构造器:
Constructor | 描述 |
---|---|
StringBuilder() | 构造一个没有字符的字符串构建器,初始容量为16个字符。 |
StringBuilder(String str) | 构造一个初始化为指定字符串内容的字符串构建器。 |
常用方法:
Modifier and Type | 方法 | 描述 |
---|---|---|
int | length() | 返回长度(字符数)。 |
String | toString() | 返回表示此顺序中的数据的字符串。 |
StringBuilder | reverse() | 导致该字符序列被序列的相反代替。 |
任意类型 | append() | 添加数据并返回 StringBuilder对象本身 |
运行结果图:
源码~~~
public class Text {
public static void main(String[] args) {
StringBuilder s = new StringBuilder(); //无参构造器,初始没有数据
StringBuilder s1 = new StringBuilder("der"); //有参构造器,直接初始数据
//1、拼接内容
s.append(12);
s.append("der~");
s.append(true);
System.out.println(s);
//支持链式编程
s1.append(12).append("hhhh");
System.out.println(s1);
//2、反转操作
s.reverse();
System.out.println(s);
//3、返回长度
System.out.println(s.length());
//4、把StringBuilder转换成String类型
String rs = s.toString();
System.out.println(rs);
}
}
为什么建议 StringBuilder为不是直接用String ?
String占用资源很多,操作效率高,对于字符串相关的操作,如频繁的拼接、修改等,建议用StringBuidler,效率更高!
注意:如果操作字符串较少,或者不需要操作,以及定义字符串变量,还是建议用String。
StringBuffer
在用法基本上与StringBuilder一致
public class Text {
public static void main(String[] args) {
StringBuffer s = new StringBuffer(); //无参构造器,初始没有数据
StringBuffer s1 = new StringBuffer("der"); //有参构造器,直接初始数据
//1、拼接内容
s.append(12);
s.append("der~");
s.append(true);
System.out.println(s);
//支持链式编程
s1.append(12).append("hhhh");
System.out.println(s1);
//2、反转操作
s.reverse();
System.out.println(s);
//3、返回长度
System.out.println(s.length());
//4、把StringBuilder转换成String类型
String rs = s.toString();
System.out.println(rs);
}
}
异同点!!!
- StringBuffer的用法与StringBuilder是一模一样的
- 但 StringBuilder是线程不安全的 StringBuffer是线程安全的
Math
代表数学,是一个工具类,里面提供的都是对数据进行操作的一些静态方法。
注意随机数:[0,1)
public class Text {
public static void main(String[] args) {
//1、取绝对值
System.out.println(Math.abs(-12));
//2、向上取整
System.out.println(Math.ceil(4.00000000001));
System.out.println(Math.ceil(4.0000));
//3、向下取整
System.out.println(Math.floor(4.9999999999999));
System.out.println(Math.floor(4.0));
//4、四舍五入
System.out.println(Math.round(3.49999999999));
System.out.println(Math.round(3.500000001));
//5、取较大值或较小值
System.out.println(Math.max(12,34));
System.out.println(Math.min(12,34));
//6、取次方
System.out.println(Math.pow(2,3));
//7、随机数 [0,1)
System.out.println(Math.random());
}
}
System
exit
- 终止程序!!!
currentTimeMillis
- 获取当前系统的时间
- 返回的是long类型的时间毫秒值:指的是从1970-1-1 0:0:0开始走到此刻的总的毫秒值,1s=1000ms
- 可以通过时间差计算程序效率(可以检测一下电脑性能哟)
- 循环100万次,我的是1s啦,嘿嘿
源码~~~(附上源码啦,可以回复评论你们的电脑运行时间呢)
public class Text {
public static void main(String[] args) {
// System.exit(0); //一般不要用,认为终止虚拟机
System.out.println("---------");
//获取当前系统的时间
//返回的是long类型的时间毫秒值:指的是从1970-1-1 0:0:0开始走到此刻的总的毫秒值,1s=1000ms
//可以通过时间差计算程序效率
long time = System.currentTimeMillis();
System.out.println(time);
for (int i = 0; i < 1000000; i++) {
System.out.println("输出了: " + i);
}
long time2 = System.currentTimeMillis();
System.out.println("程序进行了: " + (time2 - time) / 1000 +"s");
}
}
Runtime
返回与当前Java应用程序关联的运行时对象 终止当前运行的虚拟机 ;按照惯例,非零状态代码表示异常终止 获得虚拟机能够使用的处理器数 返回虚拟机中的内存总量 返回虚拟机中可用内存量 启动某个程序,并返回代表该程序的对象 关闭某个程序
BigDecimal
基本上是用于精度计算所使用。
常用API
Modifier and Type | 方法 | 描述 |
---|---|---|
BigDecimal | add(BigDecimal augend) | 返回 |
BigDecimal | subtract(BigDecimal subtrahend) | 返回 |
BigDecimal | multiply(BigDecimal multiplicand) | 返回 |
BigDecimal | divide(BigDecimal divisor, int scale, RoundingMode roundingMode) | 返回一个 |
BigDecimal | divide(BigDecimal divisor) | 返回一个 |
double | doubleValue() | 将此 |
static BigDecimal | valueOf(double val) | 转换一个 |
import java.math.BigDecimal;
import java.math.RoundingMode;
public class Text {
public static void main(String[] args) {
double a = 0.1;
double b = 0.2;
double c = a + b;
// System.out.println(c);
// BigDecimal a1 = new BigDecimal(Double.toString(a));
// BigDecimal b1 = new BigDecimal(Double.toString(b));
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1); //加法
BigDecimal c2 = a1.subtract(b1); //减法
BigDecimal c3 = a1.multiply(b1); //乘法
BigDecimal c4 = a1.divide(b1); //除法
System.out.println("加法 " +c1);
System.out.println("减法 " +c2);
System.out.println("乘法 " +c3);
System.out.println("除法 " +c4);
//除法注意事项:
BigDecimal i = BigDecimal.valueOf(0.1);
BigDecimal j = BigDecimal.valueOf(0.3);
BigDecimal k = i.divide(j, 2, RoundingMode.HALF_UP);
System.out.println("四舍五入,保留两位计算结果:(BigDecimal)"+k);
double res = k.doubleValue();
System.out.println("四舍五入,保留两位计算结果:(double)"+res);
}
}
JDK8之前的时间API
Date
Modifier and Type | 方法 | 描述 |
---|---|---|
void | setTime(long date) | 使用给定的毫秒时间值设置现有的 |
import java.util.Date;
public class Text {
public static void main(String[] args) {
//1、创建一个Date的对象,代表系统当前的时间信息
Date d = new Date();
System.out.println(d);
//2、拿到时间的毫秒值
long time = d.getTime();
System.out.println(time);
//3、把时间毫秒值转化成日期对象,(2s之后的时间是多少)
time += 2 * 1000;
Date d2 = new Date(time);
System.out.println(d2);
//4、直接把日期对象的时间通过setTime方法进行修改
Date d3 = new Date();
d3.setTime(time);
System.out.println(d3);
}
}
SimpleDateFormat
时间类型转换
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Text {
public static void main(String[] args) throws ParseException {
Date d = new Date();
System.out.println(d);
long time = d.getTime();
System.out.println(time);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE a");
String res = sdf.format(d);
System.out.println(res);
System.out.println("---------------------");
String datestr = "2024-04-14 16:47:00";
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d2 = sdf2.parse(datestr);
System.out.println(d2);
}
}
JDK8以后的时间API
获取时间(LocalDate,LocalTime,LocalDateTime)
- LocalDate:代表本地日期(年、月、日、星期)
- LocalTime:代表本地时间(时、分、秒、纳秒)
- LocalDateTime:代表本地日期、时间(年、月、曰、星期、时、分、秒、纳秒)
运行结果(基本一致,就不全部展示了):
(源码~~~)(源码可是附全了呢):
import java.time.LocalDate;
public class Text2_LocalDate {
public static void main(String[] args) {
//0、获取本地日期对象(不可变对象)
LocalDate ld = LocalDate.now(); //年 月 日
System.out.println(ld);
//1、获取日期对象中的信息
int year = ld.getYear();
int month = ld.getMonthValue();
int day = ld.getDayOfMonth();
int dayOfYear = ld.getDayOfYear();
int dayOfWeek = ld.getDayOfWeek().getValue();
System.out.println(year);
System.out.println(month);
System.out.println(day);
System.out.println(dayOfWeek);
System.out.println(dayOfYear);
System.out.println("------------");
//2、直接修改某个信息:withYear、withMonth、withDayOfMonth、withDayOfYear
LocalDate ld2 = ld.withYear(2005);
LocalDate ld3 = ld.withMonth(8);
System.out.println(ld2);
System.out.println(ld3);
System.out.println(ld);
System.out.println("-----------");
//3、把某个信息加多少:plusYears、plusMonths、plusDays、plusWeeks
LocalDate ld4 = ld.plusYears(1);
LocalDate ld5 = ld.plusMonths(2);
LocalDate ld6 = ld.plusWeeks(3);
LocalDate ld7 = ld.plusDays(4);
//4、把某个信息减多少:minusYears、minusMonths、minusDays、minusWeeks
LocalDate ld8 = ld.minusYears(1);
///5、获取指定日期的LocalDate对象: public static LocalDate of(int year,int month,int day)
LocalDate ld9 = LocalDate.of(2099, 12, 12);
System.out.println(ld);
System.out.println(ld2);
System.out.println(ld3);
System.out.println(ld4);
System.out.println(ld5);
System.out.println(ld6);
System.out.println(ld7);
System.out.println(ld8);
System.out.println(ld9);
System.out.println("-----------");
//6、判断2个日期对象,是否相等,在前还是在后:equals isBefore isAfter
System.out.println(ld8.equals(ld9));
System.out.println(ld.isAfter(ld2));
System.out.println(ld4.isBefore(ld4));
}
}
import java.time.LocalTime;
public class Text1_Localtime {
public static void main(String[] args) {
//0、获取本地时间对象
LocalTime lt = LocalTime.now();//时分秒纳秒不可变的
System.out.println(lt);
// 1、获取时间中的信息
int hour = lt.getHour();//时
int minute = lt.getMinute();//分
int second = lt.getSecond();//秒
int nano = lt.getNano();//纳秒
//2、修改时间:withHour、withMinute、withSecond、withNano
LocalTime lt3 = lt.withHour(10);
LocalTime lt4 = lt.withMinute(10);
LocalTime lt5 = lt.withSecond(10);
LocalTime lt6 = lt.withNano(10);
//3、加多少:plusHours、plusMinutes、plusSeconds、plusNanos
LocalTime lt7 = lt.plusHours(10);
LocalTime lt8 = lt.plusMinutes(10);
LocalTime lt9 = lt.plusSeconds(10);
LocalTime lt10 = lt.plusNanos(10);
//4、减多少:minusHours、minusMinutes、minusSeconds、minusNanos
LocalTime lt11 = lt.minusHours(10);
LocalTime lt12 = lt.minusMinutes(10);
LocalTime lt13 = lt.minusSeconds(10);
LocalTime lt14 = lt.minusNanos(10);
//5、获取指定时间的LocalTime对象:
//public static LocalTime of(int hour,int minute,int second)
LocalTime lt15 = LocalTime.of(12, 12, 12);
LocalTime lt16 = LocalTime.of(12, 12, 12);
//6、判断2个时间对象,是否相等,在前还是在后:equals isBefore isAfter
System.out.println(lt15.equals(lt16));
System.out.println(lt15.isAfter(lt));
System.out.println(lt15.isBefore(lt));
}
}
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class Text3_LocalDateTime {
public static void main(String[] args) {
//0、获取本地日期和时间对象。
LocalDateTime ldt = LocalDateTime.now();//年月日时分秒纳秒
System.out.println(ldt);
//1、可以获取日期和时间的全部信息
int year = ldt.getYear();//年
int month = ldt.getMonthValue();//月
int day = ldt.getDayOfMonth();//日
int dayOfYear = ldt.getDayOfYear();// 一年中的第几天
int dayOfWeek = ldt.getDayOfWeek().getValue();//获取是周几
int hour = ldt.getHour();//时
int minute = ldt.getMinute();//分
int second = ldt.getSecond();//秒
int nano = ldt.getNano();//纳秒
// 2、修改时间信息:
// withYear withMonth withDayOfMonth withDayOfYear withHour withMinute withSecond withNano
LocalDateTime ldt2 = ldt.withYear(2029);
LocalDateTime ldt3 = ldt.withMinute(59);
// 3、加多少:
// plusYears plusMonths plusDays plusWeeks plusHours plusMinutes plusSeconds plusNanos
LocalDateTime ldt4 = ldt.plusYears(2);
LocalDateTime ldt5 = ldt.plusMinutes(3);
// 4、减多少:
// minusDays minusYears, minusMonths minusWeeks minusHours minusMinutes minusSeconds minusNanos
LocalDateTime ldt6 = ldt.minusYears(2);
LocalDateTime ldt7 = ldt.minusMinutes(3);
//5、获取指定日期和时间的LocalDateTime对象
// public static LocalDateTime of(int year, int month, int dayOfMonth, int hour,
//int minute,int second.int nano0fSecond)
LocalDateTime ldt8 = LocalDateTime.of(2099, 12, 12, 12, 12, 12, 12);
LocalDateTime ldt9 = LocalDateTime.of(2099, 12, 12, 12, 12, 12, 12);
//6、 判断2个日期、时间对象,是否相等,在前还是在后:equals、isBefore、isAfter
System.out.println(ldt9.equals(ldt8));
System.out.println(ldt9.isAfter(ldt));
System.out.println(ldt9.isBefore(ldt));
//7、可以把LocalDateTime转换成LocalDate和LocalTime
// public LocalDate toLocalDate()
// public LocalTime toLocalTime()
// public static LocalDateTime of(LocalDate date, LocalTime time)
LocalDate ld = ldt.toLocalDate();
LocalTime lt = ldt.toLocalTime();
LocalDateTime ldt10 = LocalDateTime.of(ld, lt);
}
}
获取时区时间(Zoneld)
import java.time.Clock;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Text4_Zoneld_ZoneTime {
public static void main(String[] args) {
//1、ZoneId的常见方法:
//public static ZoneId systemDefault():获取系统默认的时区
ZoneId z = ZoneId.systemDefault();
System.out.println(z);
System.out.println(z.getId());
//public static Set<String>getAvailableZoneIds():获取Java支持的全部时区Id
System.out.println(ZoneId.getAvailableZoneIds());
System.out.println("---------------");
//public static oneId of(String zoneId):把某个时区id封装成ZoneId对象
ZoneId z1 = ZoneId.of("America/New_York");
//2、ZonedDateTime:带时区的时间
//public static ZonedDateTime now(ZoneId zone):获取某个时区的ZonedDateTime对象.
ZonedDateTime now = ZonedDateTime.now(z1);
System.out.println(now);
System.out.println("---------------");
ZonedDateTime now1 = ZonedDateTime.now(Clock.systemUTC());
System.out.println(now1);
System.out.println("---------------");
ZonedDateTime now2 = ZonedDateTime.now();
System.out.println(now2);
System.out.println("---------------");
}
}
Arrays
常用API
public static string toString(类型[]arr):返回数组的内容 public static int[]copyOfRange(类型[]arr,起始索引,结束索引):拷贝数组(指定范围,包前不包后) public static copyOf(类型[]arr,int newLength):拷贝数组,可以指定新数组的长度。 public static setAll(double[] array,IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。 public static void sort(类型[] arr):对数组进行排序(默认是升序排序)
import java.util.Arrays;
import java.util.function.IntToDoubleFunction;
public class Text {
public static void main(String[] args) {
//1、public static string toString(类型[]arr):返回数组的内容
int[] arr = {1, 2, 3, 4, 5, 6};
System.out.println(Arrays.toString(arr));
// 2、public static int[]copyOfRange(类型[]arr,起始索引,结束索引):拷贝数组(指定范围,包前不包后)
int[] arr2 = Arrays.copyOfRange(arr, 1, 4);
System.out.println(Arrays.toString(arr2));
// 3、public static copyOf(类型[]arr,int newLength):拷贝数组,可以指定新数组的长度。
int[] arr3 = Arrays.copyOf(arr, 10);
System.out.println(Arrays.toString(arr3));
// 4、public static setAll(double[] array,IntToDoubleFunction generator):把数组中的原数据改为新数据又存进去。
double[] prices = {49.8, 58.6, 182.4, 5.3, 1.9};
Arrays.setAll(prices, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
return prices[value] * 0.8;
}
});
System.out.println(Arrays.toString(prices));
// 5、public static void sort(类型[] arr):对数组进行排序(默认是升序排序)
Arrays.sort(prices);
System.out.println(Arrays.toString(prices));
}
}
排序
方式一:让该对象的类实现Comparable(比较规则)接口,然后重写compareTo方法,自己来制定比较规则。
方式二:使用下面这个sort方法,创建comparator比较器接口的匿名内部类对象,然后自己制定比较规则。
public static<T>void sort(T arr,Comparator<? superT>c)对数组进行排序(支持自定义排序规则)
排序规则:
//约定1:认为左边的对象大于右边的对象,请你返回正整数。 //约定2:认为左边的对象小于右边的对象,请你返回负整数。 //约定3:认为左边的对象等于右边的对象,请你返回0。
注意:如果是double数据进行比较,不可以直接通过做减法,进行得出结果,可以通过方法:
public static int compare(double d1, double d2)
方法一:
运行结果:
实现源码
import java.util.Arrays;
public class Text {
public static void main(String[] args) {
Student [] students = new Student[4];
students[0] = new Student("der",19,180.2);
students[1] = new Student("str",17,179.3);
students[2] = new Student("wkx",16,168.5);
students[3] = new Student("tjl",15,180.1);
//让该对象的类实现Comparable(比较规则)接口,然后重写compareTo方法,自己来制定比较规则。
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
public class Student implements Comparable<Student> {
private String name;
private int age;
private double height;
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
//指定比较规则
//this(左) o(右)
@Override
public int compareTo(Student o) {
//约定1:认为左边的对象大于右边的对象,请你返回正整数。
//约定2:认为左边的对象小于右边的对象,请你返回负整数。
//约定3:认为左边的对象等于右边的对象,请你返回0。
//按照年龄
// if (this.age > o.age) {
// return 1;
// } else if ((this.age < o.age)) {
// return -1;
// }
// return 0;
// return this.age - o.age; //简化版(升序)
return o.age - this.age; //简化版(降序)
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
方法二:
运行结果;
实现源码:
import java.util.Arrays;
import java.util.Comparator;
public class Text {
public static void main(String[] args) {
Student[] students = new Student[4];
students[0] = new Student("der", 19, 180.2);
students[1] = new Student("str", 17, 179.3);
students[2] = new Student("wkx", 16, 168.5);
students[3] = new Student("tjl", 15, 180.1);
//让该对象的类实现Comparable(比较规则)接口,然后重写compareTo方法,自己来制定比较规则。
// Arrays.sort(students);
// System.out.println(Arrays.toString(students));
//public static<T>void sort(T arr,Comparator<? superT>c)对数组进行排序(支持自定义排序规则)
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
//指定比较规则:左o1,右o2
//约定1:认为左边的对象大于右边的对象,请你返回正整数。
//约定2:认为左边的对象小于右边的对象,请你返回负整数。
//约定3:认为左边的对象等于右边的对象,请你返回0。
//注意如果是double数据,不能直接通过减法,进行计算,会出现错误
// if (o1.getHeight() > o2.getHeight()) {
// return 1;
// } else if ((o1.getHeight() < o2.getHeight())) {
// return -1;
// }
// return 0;//升序
// return Double.compare(o1.getHeight(),o2.getHeight());//升序
return Double.compare(o2.getHeight(),o1.getHeight());//降序
}
});
System.out.println(Arrays.toString(students));
}
}
public class Student implements Comparable<Student> {
private String name;
private int age;
private double height;
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
//指定比较规则
//this(左) o(右)
@Override
public int compareTo(Student o) {
//约定1:认为左边的对象大于右边的对象,请你返回正整数。
//约定2:认为左边的对象小于右边的对象,请你返回负整数。
//约定3:认为左边的对象等于右边的对象,请你返回0。
//按照年龄
// if (this.age > o.age) {
// return 1;
// } else if ((this.age < o.age)) {
// return -1;
// }
// return 0;
// return this.age - o.age; //简化版(升序)
return o.age - this.age; //简化版(降序)
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
Lambda
Lambda表达式是JDK 8开始新增的一种语法形式;
作用:用于简化匿名内部类的代码写法。
注意:Lambda表达式只能简化函数式接口的匿名内部类!!!
什么是函数式接口?
- 有且仅有一个抽象方法的接口。
- 注意:将来我们见到的大部分函数式接口,上面都可能会有一个@Functionallnterface的注解,有该注解的接口就必定是函数式接口。
//简化前
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(), o2.getHeight());
}
});
//简化后
Arrays.sort(students, (Student o1, Student o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight());
});
//简化前
Arrays.setAll(prices, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
return prices[value] * 0.8;
}
});
//简化后
Arrays.setAll(prices, (int value) -> {
return prices[value] * 0.8;
});
Lambda表达式的省略写法(进一步简化Lambda表达式的写法)
- 参数类型可以省略不写,
- 如果只有一个参数,参数类型可以省略,同时()也可以省略。
- 如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写
原始代码(双参数):
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(), o2.getHeight());
}
});
一次简化:
Arrays.sort(students, (Student o1, Student o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight());
});
二次简化(参数类型可以省略不写):
Arrays.sort(students, (o1, o2) -> {
return Double.compare(o1.getHeight(), o2.getHeight());
});
三次简化(如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写):
Arrays.sort(students, (o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight()));
原始代码(单参数):
Arrays.setAll(prices, new IntToDoubleFunction() {
@Override
public double applyAsDouble(int value) {
return prices[value] * 0.8;
}
});
一次简化:
Arrays.setAll(prices, (int value) -> {
return prices[value] * 0.8;
});
二次简化(参数类型可以省略不写):
Arrays.setAll(prices, ( value) -> {
return prices[value] * 0.8;
});
三次简化(如果只有一个参数,参数类型可以省略,同时()也可以省略。):
Arrays.setAll(prices, value -> {
return prices[value] * 0.8;
});
四次简化(如果Lambda表达式中的方法体代码只有一行代码,可以省略大括号不写,同时要省略分号!此时,如果这行代码是return语句,也必须去掉return不写):
Arrays.setAll(prices, value -> prices[value] * 0.8);
方法引用
静态方法引用
- 类名::静态方法。
使用场景
- 如果某个Lambda表达式里只是调用一个静态方法,并且前后参数的形式一致,就可以使用静态方法引用。
改之前:
Arrays.sort(students, (o1, o2) -> Double.compare(o1.getAge(), o2.getAge()));
改制后:
Arrays.sort(students, CompareByData::comoareByAge);
实例方法的引用
- 对象名::实例方法。
使用场景
如果某个Lambda表达式里只是调用一个实例方法,并且前后参数的形式一致,就可以使用实例方法引用。
特定类型的方法引用
- 类型::方法。
使用场景
- 如果某个Lambda表达式里只是调用一个实例方法,并且前面参数列表中的第一个参数是作为方法的主调后面的所有参数都是作为该实例方法的入参的,则此时就可以使用特定类型的方法引用。
原码:
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareToIgnoreCase(o2); //忽略大小写进行对比
}
});
第一次化简:
Arrays.sort(names, (o1, o2) -> o1.compareToIgnoreCase(o2));
第二次化简(实例方法引用):
Arrays.sort(names, String::compareToIgnoreCase);
构造器引用
- 类名::new。
使用场景
- 如果某个Lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用
原码:
CreateCar cc = new CreateCar() {
@Override
public Car create(String name, double price) {
return new Car(name, price);
}
};
第一次化简:
CreateCar cc = ( name, price) -> new Car(name, price);
第二次化简:
CreateCar cc = Car::new;
面对对象高级
static
封装
用static修饰成员变量
有static修饰的变量
类名.类变量(推荐)
对象名.类变量(不推荐)
属于类, 在内存中只有一份,通过对象,或者直接通过类名改变都是直接将其改变(类似于全局变量)
无static修饰的变量
对象.实例变量(类名.实例变量 (是不合法的,不能用))
public class student {
static String name;
int age;
}
public class Test {
public static void main(String[] args) {
student.name = "ljt";
student s1 = new student();
s1.name = "der";
//因为这个有点类似于全局变量
System.out.println(s1.name); //der
System.out.println(student.name); //der
}
}
类变量的应用场景
成员变量有几种?各自在什么情况下定义?
- 类变量:数据只需要一份,且需要被共享时(访问,修改)
- 实例变量:每个对象都要有一份,数据各不同(如:name、score、age)
访问自己类中的类变量,是否可以省略类名不写?
- 可以的
- 注意:在某个类中访问其他类里的类变量,必须带类名访问
public class number {
public static int num;
public number(){
number.num++;
}
}
public class Text {
public static void main(String[] args) {
number n1 = new number();
number n2 = new number();
number n3 = new number();
number n4 = new number();
System.out.println(number.num);
}
}
static修饰成员方法
public class Student {
double score;
public static void printHello() {
System.out.println("Hello World!");
System.out.println("Hello World!");
}
public void printPass() {
System.out.println("成绩:" + (score >= 60 ? "及格" : "不及格"));
}
}
public class Text {
public static void main(String[] args) {
Student.printHello();
Student s =new Student();
s.printHello();
s.printPass();
//Student.printPass(); //报错
}
}
工具类
类方法有啥应用场景?
——可以用来设计工具类。
工具类是什么,有什么好处?
- 工具类中的方法都是类方法,每个类方法都是用来完成一个功能的。
- 提高了代码的复用性;调用方便,提高了开发效率。
为什么工具类要用类方法,而不是用实例方法?
——实例方法需要创建对象来调用,会浪费内存。
工具类定义时有什么要求?
——工具类不需要创建对象,建议将工具类的构造器私有化。
import java.util.Random;
public class MyUtil {
private MyUtil(){
//一般将类名私有,不让外界调用,而是直接调用类方法。
}
public static String createCode(int n){
String code = "";
String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789";
Random r = new Random();
for (int i = 0; i < n; i++) {
int index = r.nextInt(data.length());
code += data.charAt(index);
}
return code;
}
}
public class Text {
public static void main(String[] args) {
System.out.println(MyUtil.createCode(4));
}
}
static注意事项
public class Student {
static String schoolName;
double score;
public static void printHello(){
schoolName ="黑马";
printHelloWorld2();
//System.out.println(score);// 报错的
// printPass();// 报错的
// System.out.println(this);// 报错的
}
//类方法
public static void printHelloWorld2(){
}
public void printPass(){
schoolName ="黑马2";
printHelloWorld2();
System.out.println(score);
printPass2();
}
public void printPass2(){
}
}
public class Text {
public static void main(String[] args) {
//1、类方法中可以直接访问类的成员,不可以直接访问实例成员。
//2、实例方法中既可以直接访问类成员,也可以直接访问实例成员。
//3、实例方法中可以出现this关键字,类方法中不可以出现this关键字
}
}
单例类
饿汉式单变量
拿到对象的时候,早就创建好对象
//单例类(饿汉式单变量)
public class A {
private static A a = new A();
private A() {
}
public static A getA() {
return a;
}
}
懒汉式单变量
拿到对象的时候,才创建对象
//单例类(懒汉式单变量)
public class B {
private static B b;
//先把构造器私有
private B() {
}
public static B getB() {
if (b == null) {
System.out.println("第一次建立对象!");
b = new B();
}
return b;
}
}
继承
私有的变量,对象不可以被访问
父类的对象不能访问子类中的变量
什么是继承?继承后有啥特点 ?
- 继承就是用extends关键字,让一个类和另一个类建立起一种父子关系。
- 子类可以继承父类非私有的成员。
带继承关系的类,Java会怎么创建它的对象?对象创建出来后,可以直接访问哪些成员?
- 带继承关系的类,java会用类和其父类,这多张设计图来一起创建类的对象。
- 对象能直接访问什么成员,是由子父类这多张设计图共同决定的,这多张设计图对外暴露了什么成员,对象就可以访问什么成员。
继承的优点:
减少重复代码的编写,提高代码的复用性。
//父类
public class A {
//公开成员
public int i;
public void print1() {
System.out.println("print1");
}
//私有成员
private int j;
private void print2() {
System.out.println("print2");
}
}
//子类
public class B extends A {
public int k;
//子类可以直接访问父类中的公开的成员
public void print3() {
System.out.println("print3");
System.out.println(i);
print1();
//System.out.println(j);
//print2();
}
}
public class Text {
public static void main(String[] args) {
B b = new B();
System.out.println(b.i);
//System.out.println(b.j);
//父类的对象不能访问子类的变量
//A a = new A();
//System.out.println(a.k);
}
}
继承的实例练习
public class Text {
public static void main(String[] args) {
Teacher t =new Teacher();
t.setName("der");
t.setSkill("Java Skill");
System.out.println(t.getName());
System.out.println(t.getSkill());
t.printInfo();
}
}
public class people {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Teacher extends people {
private String skill;
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
public void printInfo() {
System.out.println(getName() + "具备的技能:" + skill);
}
}
权限修饰符
protected
private不能访问
缺省,protected,public可以访问
单继承
所有的类自动继承object类,所以object类是所有类的父类或者是爷(祖宗)类
Java不允许多继承,但是支持多层继承
多继承(报错)
多层继承(没问题)
方法重写
方法重写是什么?
子类写了一个方法名称,形参列表与父类某个方法一样的方法去覆盖父类的该方法
重写方法有哪些注意事项?
- 建议加上:@Override注解,可以校验重写是否正确,同时可读性好。
- 子类重写父类方法时,访问权限必须大于或者等于父类被重写的方法的权限。
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写。
方法重写有啥应用场景?
当子类觉得父类的方法不好用,或者不满足自己的需求时,就可以用方法重写
什么是方法重写?
- 当子类觉得父类中的某个方法不好用,或者无法满足自己的需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。
- 注意:重写后,方法的访问,Java会遵循就近原则。
方法重写的其它注意事项
- 重写小技巧:使用0verride注解,他可以指定java编译器,检查我们方法重写的格式是否正确,代码可读性也会更好。
- 子类重写父类方法时,访问权限必须大于或者等于父类该方法的权限(public>protected>缺省)。
- 重写的方法返回值类型,必须与被重写方法的返回值类型一样,或者范围更小。
- 私有方法、静态方法不能被重写,如果重写会报错的。
方法重写前:
方法重写后:
通过 @Override 检查方法重写的正确性
如果有问题的话就会报错
一般口诀:“申明不变,重新实现”
子类变量(成员方法)访问注意事项:
this是访问本类中的变量 , super是访问父类中变量 , 直接引用的话遵循就近原则(本类(子类)的对象中的变量>子类中的变量>父类中的变量)
public class Text {
public static void main(String[] args) {
Teacher t = new Teacher("der",18,"java");
System.out.println(t.getName());
System.out.println(t.getAge());
System.out.println(t.getSkill());
}
}
//父类
public class People {
private String name;
private int age;
public People() {
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
//子类
public class Teacher extends People{
private String skill;
public Teacher(String name,int age,String skill){
super(name,age);
this.skill = skill;
}
public String getSkill() {
return skill;
}
public void setSkill(String skill) {
this.skill = skill;
}
}
子类构造器的注意事项:
子类构造器的特点:
子类的全部构造器,都会先调用父类的构造器,再执行自己。
子类构造器是如何实现调用父类构造器的:
默认情况下,子类全部构造器的第一行代码都是super()(写不写都有),它会调用父类的无参数构造器。
如果父类没有无参数构造器,则我们必须在子类构造器的第一行手写super(),指定去调用父类的有参数构造器。
补充知识:this(...)调用兄弟构造器
任意类的构造器中,是可以通过this(...)去调用该类的其他构造器的。
注意:在同一个构造器中不能同时使用this和super调用两个构造器!!
e.g:
子类构造器:
public class Text {
public static void main(String[] args) {
//子类的所有构造器都是先调用父类的构造器再调用自己的构造器
D d = new D();
D d1 = new D(66);
}
}
class F {
//public F() {
// System.out.println("父类的无参构造器执行");
//}
public F(int s){
}
}
class D extends F {
public D() {
super(6);
//super(); //默认存在的 但是默认调用无参构造器
System.out.println("子类的无参构造器执行");
}
public D(int num) {
super(6);
System.out.println("子类的有参构造器执行");
}
}
this调用兄弟构造器:
public class Text2 {
public static void main(String[] args) {
}
}
class Student{
private String name;
private int age;
private String School;
public Student() {
}
// public Student(String name, int age) {
// this.name = name;
// this.age = age;
// this.School = "der";
// }
public Student(String name, int age) {
this(name,age,"der");
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
School = school;
}
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 String getSchool() {
return School;
}
public void setSchool(String school) {
School = school;
}
}
多态
多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
多态的具体代码体现:
People p1 = new student();
p1.run();
People p2 = new Teacher();
p2.run();
多态的前提
- 有继承/实现关系;
- 存在父类引用子类对象;
- 存在方法重写 。
多态的一个注意事项
- 多态是对象、行为的多态,|ava中的属性(成员变量)不谈多态,
识别技巧: 编译看左边,运行看右边
public class Text{
public static void main(String[] args) {
People p1 = new Student();
p1.run();
System.out.println(p1.name);
People p2 = new Teacher();
p2.run();
System.out.println(p2.name);
}
}
public class People {
public String name = "People";
public void run(){
System.out.println("People run");
}
}
public class Teacher extends People{
public String name = "Teacher";
@Override
public void run(){
System.out.println("Teacher run~~");
}
}
public class Student extends People{
public String name = "Student";
@Override
public void run(){
System.out.println("Student run~~");
}
}
使用多态的好处
- 在多态形式下,右边对象是解耦合的,更便于扩展和维护
- 定义方法时,使用父类类型的形参,可以接收一切子类对象,扩展性更强、更便利。
多态下会产生的一个问题
多态下不能使用子类的独有功能。
可以通过强制类型转换进行调用,强制类型转换可能存在的问题:编译阶段有继续或者实现关系就可以强制转换,但是运行时可能出现类型转换异常 , 运行时出现了:ClassCastException
解决方法:
先通过instanceof
import polymorphism_d1.People;
import polymorphism_d1.Student;
import polymorphism_d1.Teacher;
public class Text {
public static void main(String[] args) {
People p1 = new Student();
p1.run();
People s = new Student();
go(s);
People t =new Teacher();
go(t);
}
public static void go(People p){
if(p instanceof Student){
Student s = (Student) p;
s.test();
}
else{
Teacher t = (Teacher) p;
t.teach();
}
}
}
public class People {
public String name = "People";
public void run(){
System.out.println("People run");
}
}
public class Teacher extends People{
public String name = "Teacher";
@Override
public void run(){
System.out.println("Teacher run~~");
}
}
public class Student extends People{
public String name = "Student";
@Override
public void run(){
System.out.println("Student run~~");
}
}
Final(反转关键字)
final关键字是最终的意思,可以修饰(类、方法、变量)
- 修饰类:该类被称为最终类,特点是不能被继承了。
- 修饰方法:该方法被称为最终方法,特点是不能被重写了。
- 修饰变量:该变量只能被赋值一次。
修饰类:(用处不多,一般仅用于工具类)
修饰方法:
修饰变量:(局部变量,成员变量)
用于保护数据,防止被改变
public static final string SCHOOL_NAME ="der";
常量:public static final修饰的成员变量,建议名称全部大写,多个单词下划线连接
修饰类型
注意事项:
- final修饰基本类型的变量,变量存储的数据不能被改变。
- final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的
e.g:
- 地址不能改变
- 地址所指内容可以改变
常量
static final
- 使用了 static final修饰的成员变量就被称为常量;
- 作用:通常用于记录系统的配置信息
- 注意!常量名的命名规范:建议使用大写英文单词,多个单词使用下划线连接起来
使用常量记录系统配置信息的优势、执行原理
- 代码可读性更好,可维护性也更好。
- 程序编译后,常量会被“宏替换”:出现常量的地方全部会被替换成其记住的字面,这样可以保证使用常量和直接用字面量的性能是一样的。
抽象类
抽象类的注意事项、特点
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有。
- 抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
抽象方法:必须用abstract修饰,只有方法签名,一定不能有方法体
抽象类不能创造对象
抽象类的应用场景和好处是什么?
父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们抽出这样的抽象类,就是为了更好的支持多态。
public class Text {
public static void main(String[] args) {
Animal c = new Cat();
Animal d = new Dog();
c.setName("小猫");
c.cry();
d.setName("小狗");
d.cry();
}
}
public abstract class Animal {
private String name;
public abstract void cry();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Dog extends Animal{
@Override
public void cry() {
System.out.println(getName()+"汪汪汪叫~~");
}
}
public class Cat extends Animal{
@Override
public void cry() {
System.out.println(getName()+"喵喵喵叫~");
}
}
模板方法设计模式
模板方法设计模式的写法
- 定义一个抽象类。
- 在里面定义2个方法
一个是模板方法:把相同代码放里面去
一个是抽象方法:具体实现交给子类完成。建议用final关键字进行修饰模板方法
!!防止子类修改模板!!
public class Text {
public static void main(String[] args) {
People s= new Student();
s.write();
System.out.println("------------------------------");
People t = new Teacher();
t.write();
}
}
public abstract class People {
public final void write(){
System.out.println("/t/t/t哈哈哈哈哈哈哈哈哈哈哈");
System.out.println("/t/t/t哈哈哈哈哈哈哈哈哈哈哈");
System.out.println(writeMain());
System.out.println("/t/t/thhhhhhhhhhhhhhhhh");
System.out.println("/t/t/thhhhhhhhhhhhhhhhh");
}
public abstract String writeMain();
}
public class Teacher extends People{
@Override
public String writeMain() {
return "/t/t/t/t teacher";
}
}
public class Student extends People{
@Override
public String writeMain() {
return "/t/t/t/t student";
}
}
接口
、
变量默认为常量
成员方法默认为是抽象方法,不能有主体。
修饰符 class 实现类 implements 接口1,接口2,接口3,…{}
注意:接口不能创建对象:接口是用来被类实现(implements)的,实现接口的类称为实现类
一个类可以实现多个接口(接口可以理解成干爹),实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类。
接口的好处(重点)
- 弥补了类单继承的不足,一个类同时可以实现多个接口。
- 让程序可以面向接口编程,这样程序员就可以灵活方便的切换各种业务实现
示例项目
项目需求:
请设计一个班级学生的信息管理模块:学生的数据有:姓名、性别、成绩
功能1:要求打印出全班学生的信息;
功能2:要求打印出全班学生的平均成绩
注意! 以上功能的业务实现是有多套方案的,比如:
第1套方案:能打印出班级全部学生的信息;能打印班级全部学生的平均分
第2套方案:能打印出班级全部学生的信息(包含男女人数);能打印班级全部学生的平均分(要求是去掉最高分、最低分)
要求:系统可以支持灵活的切换这些实现方案。
public class Text {
public static void main(String[] args) {
ClassManager classManager = new ClassManager();
classManager.printInfo();
classManager.printScore();
}
}
import java.util.ArrayList;
public class ClassManager {
private ArrayList<Student> students = new ArrayList<>();
private StudentOperator studentOperator = new StudenOperatorImpl2();
public ClassManager() {
students.add(new Student("der", '男', 99));
students.add(new Student("啊", '男', 70));
students.add(new Student("嗯", '女', 100));
students.add(new Student("哼", '女', 74));
}
public void printInfo() {
studentOperator.printAllInfo(students);
}
public void printScore() {
studentOperator.printAverageScore(students);
}
}
public interface StudentOperator {
void printAllInfo(ArrayList<Student> students);
void printAverageScore(ArrayList<Student>students);
}
public class StudenOperatorImpl1 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("--------------全班成绩--------------");
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:" + s.getName() + " 性别:" + s.getSex() + " 成绩:" + s.getScore());
}
System.out.println("----------------------------------");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore = 0.0;
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
allScore += s.getScore();
}
System.out.println("平均分:" + (allScore) / students.size());
}
}
public class StudenOperatorImpl2 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("--------------全班成绩--------------");
int count1 = 0; //男
int count2 = 0; //女
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println("姓名:" + s.getName() + " 性别:" + s.getSex() + " 成绩:" + s.getScore());
if (s.getSex() == '男') {
count1++;
} else {
count2++;
}
}
System.out.println("男生人数为:" + count1 + " 女生人数为:" + count2);
System.out.println("班级总人数:" + students.size());
System.out.println("----------------------------------");
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double allScore = 0.0;
double max = students.get(0).getScore();
double min = students.get(0).getScore();
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
allScore += s.getScore();
if (s.getScore() > max) max = s.getScore();
if (s.getScore() < min) min = s.getScore();
}
System.out.println("学生的最高分是;" + max);
System.out.println("学生的最低分是;" + min);
System.out.println("平均分:" + (allScore - max - min) / (students.size() - 2));
}
}
JDK8开始,接口中新增了哪些方法?
- 默认方法:使用default修饰,使用实现类的对象调用。
- 静态方法:static修饰,必须用当前接口名调用
- 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
- 他们都会默认被public修饰。
接口的多继承
作用:便于实现类去实现
public class Text {
}
interface A {
void test1();
}
interface B {
void test2();
}
interface C {
}
interface D extends A, B, C {
}
class E implements D{
@Override
public void test1() {
}
@Override
public void test2() {
}
}
注意事项:
- 一个接口继承多个接口,如果多个接口中存在方法签名冲突,则此时不支持多继承,
interface A { void test1(); } interface B { String test1(); } interface D extends A, B { }
- 一个类实现多个接口,如果多个接口中存在方法签名冲突,则此时不支持多实现。
interface A { void test1(); } interface B { String test1(); } class E implements A, B { }
- 一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会优先用父类的。
public class Text { public static void main(String[] args) { E e =new E(); e.func(); } } interface I { default void func() { System.out.println("接口程序"); } } class Fu { public void func() { System.out.println("父类程序"); } } class E extends Fu implements I { }
- 一个类实现了多个接口,多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
public class Text { public static void main(String[] args) { N n = new N(); n.func(); } } interface IT1{ default void func(){ System.out.println("IT1"); } } interface IT2{ default void func(){ System.out.println("IT2"); } } class N implements IT1,IT2{ @Override public void func() { System.out.println("Slef"); } }
内部类
成员内部类
成员内部类是什么?如何创建其对象?
- 就是类中的一个普通成员,类似前面我们学过的普通成员变量、成员方法
- 外部类名.内部类名 对象名 =new 外部类(...).new 内部类(...);
成员内部类的实例方法中,访问其他成员有啥特点?
- 可以直接访问外部类的实例成员、静态成员
- 可以拿到当前外部类对象,格式是:外部类名.this.名
- 访问内部类的方法中的成员直接引用,格式:名
- 访问内部类的成员,格式: this.名,
- 访问外部类的成员,格式:外部类名.this.名
System.out.println(a); System.out.println(this.a); System.out.println(Outer.this.a);
public class Text {
public static void main(String[] args) {
Outer.Inner t = new Outer().new Inner();
t.printA();
}
}
public class Outer {
private int a = 100;
public class Inner{
private String name;
private int a = 88;
public void printA(){
int a = 10;
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
}
静态内部类
什么是静态内部类?
有static修饰的内部类,属于外部类自己持有。
创建对象的格式:
外部类名.内部类名 对象名 =new 外部类.内部类(...)
Outer.inner in= new Outer.inner();
静态内部类中访问外部类成员的特点
可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员。
局部内部类
匿名内部类
就是一种特殊的局部内部类;所谓匿名:指的是程序员不需要为这个类声明名字。
Animal a = new Animal() { @Override public void cry() { } };
特点:匿名内部类本质就是一个子类,并会立即创建出一个子类对象。
作用:用于更方便的创建一个子类对象。
没有使用匿名内部类的时候这样写:
public class Text {
public static void main(String[] args) {
Animal a =new Cat();
a.cry();
}
}
class Cat extends Animal{
@Override
public void cry() {
System.out.println("小猫喵喵叫~");
}
}
abstract class Animal{
public abstract void cry();
}
改用匿名内部类后:
一定要注意要加分号!!!!!
public class Text {
public static void main(String[] args) {
// Animal a =new Cat();
// a.cry();
Animal a = new Animal() {
@Override
public void cry() {
System.out.println("小猫喵喵叫~");
}
};
a.cry();
}
}
abstract class Animal{
public abstract void cry();
}
匿名内部类实际上是建立一个子类
通常通过调用方法进行使用,匿名内部类直接可以作为对象直接进行调用
public class Text {
public static void main(String[] args) {
// Animal a =new Cat();
// a.cry();
go( new Animal() {
@Override
public void cry() {
System.out.println("小猫喵喵叫~");
}
});
}
public static void go(Animal a){
System.out.println("----------start------------");
a.cry();
}
}
abstract class Animal{
public abstract void cry();
}
匿名内部类最终的核心目的是:简化代码
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "登录一下");
}
});
简化后:
btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "登录一下"));
实例项目展示:
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Text {
public static void main(String[] args) {
// 拓展:搞清楚匿名内部类在开发中的真实使用场景。
// GUI编程
// 1、创建窗口
JFrame win = new JFrame("登录界面");
JPanel panel = new JPanel();
win.add(panel);
JButton btn = new JButton("登录");
panel.add(btn);
//给按钮绑定单击事件监听器
// 最终的核心目的是:简化代码
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win, "登录一下");
}
});
btn.addActionListener(e -> JOptionPane.showMessageDialog(win, "登录一下"));
win.setSize( 400,400);
win.setLocationRelativeTo(null);
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
win.setVisible(true);
}
}
枚举类
- 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象。
- 枚举类的构造器都是私有的(写不写都只能是私有的),因此,枚举类对外不能创建对象。
- 枚举都是最终类,不可以被继承。
- 枚举类中,从第二行开始,可以定义类的其他各种成员
- 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法。
还包括很多额外的API
e.g:
B[] as = B.values(); //拿到全部的对象
B a3 = B.valueOf("Z"); //通过枚举的常量名得到这个枚举对象
System.out.println(a3.name());
System.out.println(a3.ordinal()); //获得枚举对象的索引
罗列的名称都是常量,且记录序数。
必须是先定义一个无参构造器,再定义一个有参构造器
如果使用抽象办法,可以直接进行重写。
Y("der") {
@Override
public void go() {
System.out.println(getName()+"run");
}
};
基础用法:
public class Text {
public static void main(String[] args) {
B b1 = B.X;
System.out.println(b1);
A a2 = A.Y;
a2.go();
//额外的API
B[] as = B.values(); //拿到全部的对象
B a3 = B.valueOf("Z"); //通过枚举的常量名得到这个枚举对象
System.out.println(a3.name());
System.out.println(a3.ordinal()); //获得枚举对象的索引
}
}
public enum A {
X {
@Override
public void go() {
}
},Y("der") {
@Override
public void go() {
System.out.println(getName()+" run");
}
};
public abstract void go();
private String name;
//必须是先定义一个无参构造器,再定义一个有参构造器
A() {
}
A(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public enum B {
X,Y,Z;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
实际演练:
- 用来表示一组信息,然后作为参数进行传输,
选择定义一个一个的常量来表示一组信息,并作为参数传输
- 参数值不受约束
选择定义枚举表示一组信息,并作为参数传输
- 代码可读性好,参数值得到了约束,对使用者更友好,建议使用!
但是与直接定义常量各有优缺点,所以相互不可直接取代。
public class Text {
//通过枚举类进行调用
public static void main(String[] args) {
//做信息标志和分类
check(Constant2.BOY);
check(Constant2.GIRL);
}
public static void check(Constant2 sex){
switch (sex){
case BOY:
System.out.println("case man");
break;
case GIRL:
System.out.println("case woman");
break;
}
}
//通过常量进行调用
public static void main(String[] args) {
//做信息标志和分类
check(Constant.BOY);
check(Constant.GIRL);
}
public static void check(int sex){
switch (sex){
case Constant.BOY:
System.out.println("case man");
break;
case Constant.GIRL:
System.out.println("case woman");
break;
}
}
}
//定义常量
public class Constant {
public static final int BOY = 0;
public static final int GIRL = 1;
}
//枚举类
public enum Constant2 {
BOY,GIRL
}
泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口,泛型方法、它们统称为泛型。
作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!这样可以避免强制类型转换,及其可能出现的异常。
泛型的本质:把具体的数据类型作为参数传给类型变量。
如果直接强制转化会出问题:
import java.util.ArrayList;
public class Text {
public static void main(String[] args) {
// ArrayList list = new ArrayList();
// list.add("java1");
// list.add("java2");
// list.add("java3");
// list.add("java4");
// list.add(new Cat());
// for (int i = 0; i < list.size(); i++) {
// String e = (String) list.get(i);
// System.out.println(e);
// }
ArrayList<String> list1 = new ArrayList<>();
list1.add("java1");
list1.add("java2");
list1.add("java3");
list1.add("java4");
// list1.add(new Cat());
}
}
泛型类
基本定义方式:
public class MyArrayList<E> {
private Object[] arr = new Object[10];
private int size;
public boolean add(E e) {
arr[size++] = e;
return true;
}
public E get(int index){
return (E) arr[index];
}
}
多类型定义:
public class MyClass<T, E> {
public void put(T t, E e) {
}
}
限制类型定义:
public class MyClass2 <T extends Animal> {
}
泛型接口
类似于接口,需要加上泛型的格式。
代码框架
import java.util.ArrayList;
public class Text {
public static void main(String[] args) {
//需要进行处理老师和学生的信息
}
}
public class Student {
}
public class Teacher {
}
public class StudentData implements Data<Student>{
@Override
public void add(Student student) {
}
@Override
public ArrayList<Student> getByName(String name) {
return null;
}
}
public class TeacherData implements Data<Teacher>{
@Override
public void add(Teacher teacher) {
}
@Override
public ArrayList<Teacher> getByName(String name) {
return null;
}
}
//泛型接口
public interface Data<T> {
void add(T t);
ArrayList<T> getByName(String name);
}
泛型方法
修饰符<类型变量 , 类型变量 ......>返回类型 方法名(形参列表){}
//错误代码!!!!
public static void main(String[] args) {
String res = test("java");
System.out.println(res);
ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new BENZ());
go(cars);
ArrayList<BENZ> benzs = new ArrayList<>();
benzs.add(new BENZ());
benzs.add(new BENZ());
go(benzs);
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
go(bmws);
}
public static void go(ArrayList<Car> cars) {
}
因为Car与BENZ类型不同,不能统一到一起,但是可以通过泛型合到一起
修改后:通过泛型进行合并(可以通过extends进行类型限定,防止其他类型接入)
//正确代码!!!!
public static void main(String[] args) {
String res = test("java");
System.out.println(res);
ArrayList<Car> cars = new ArrayList<>();
cars.add(new BMW());
cars.add(new BENZ());
go(cars);
ArrayList<BENZ> benzs = new ArrayList<>();
benzs.add(new BENZ());
benzs.add(new BENZ());
go(benzs);
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
go(bmws);
}
public static <T extends Car> void go(ArrayList<T> cars) {
}
通配符 “?” 在使用泛型的时候可以代表一切类型
就是"?",可以在“使用泛型”的时候表示一切类型;E K T V 是在定义泛型的时候使用的。
public static void go(ArrayList<? extends Car>cars){
}
这样同样可以把其他类型的数据阻挡出去
作用与相同:
public static <T extends Car> void go(ArrayList<T> cars) {
}
上限: T ? extends Car
T 必须是Car或者是Car的子类
下限: T ? super Car
T 必须是Car或者是Car的父类
泛型的擦除问题和注意事项
- 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除。
- 泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
正则表达式
就是由一些特定的字符组成,代表的是一个规则。
作用一:
用来校验数据格式是否合法作用二:
在一段文本中查找满足要求的内容
public class Test {
public static void main(String[] args) {
System.out.println(checkQQ(null));
System.out.println(checkQQ("198237465"));
System.out.println(checkQQ("198237g465"));
}
public static boolean checkQQz(String qq) {
return qq != null && qq.matches("[1-9]\\d{5,19}");
}
// public static boolean checkQQ(String qq) {
//
// if (qq == null || qq.startsWith("0") || qq.length() < 6 || qq.length() > 20) {
// return false;
// }
// for (int i = 0; i < qq.length(); i++) {
// char ch = qq.charAt(i);
// if (ch < '0' || ch > '9')
// return false;
// }
// return true;
// }
}
重点在于有个印象
public class Test2 {
public static void main(String[] args) {
//1、
System.out.println("a".matches("[abc]"));//[abc]只能匹配a、b、c
System.out.println("e".matches("[abcd]"));
System.out.println("d".matches("[^abc]"));//[^abc]不能是abc
System.out.println("a".matches("[^abc]"));
System.out.println("b".matches("[a-zA-Z]"));//[a-zA-Z]只能是a-z A-Z的字符
System.out.println("2".matches("[a-zA-Z]"));
System.out.println("k".matches("[a-z&&[^bc]]"));//: a到z,除了b和c
System.out.println("b".matches("[a-z&&[^bc]]"));
System.out.println("ab".matches("[a-zA-Z0-9]"));// false 注意:以上带【内容]的规则都只能用于匹配单个字符
System.out.println("-----------------");
//2、预定义字符(只能匹配单个字符)·\d \D \s \S \w \W
System.out.println("徐".matches("."));//.可以匹配任意字符
System.out.println("徐徐".matches("."));
// \ 在Java中有特殊意义:\n换行 \t代表一个缩进
// 如果在java中,希望\就是\必须转义
// 例如:“\d”要代表\d,需要写成:"\\d"
System.out.println("\""); // "
System.out.println("3".matches("\\d")); // \d: 0-9
System.out.println("a".matches("\\d"));
System.out.println(" ".matches("\\s"));// \s:代表一个空白字符
System.out.println("a".matches("\s"));
System.out.println("a".matches("\\S")); // \S:代表一个非空白字符
System.out.println("".matches("\\s"));
System.out.println("a".matches("\\w"));//w:[a-zA-Z_0-9]
System.out.println(" ".matches("\\w"));
System.out.println("徐".matches("\\W")); //[^\w]不能是a-zA-Z_0-9
System.out.println("a".matches("\\w"));
System.out.println("23232".matches("\\d"));//false 注意:以上预定义字符都只能匹配单个字符。
System.out.println("-----------------");
// 3、数量词: ? * + {n} {n, } {n,m}
System.out.println("a".matches("\\w?"));//?代表0次或1次
System.out.println("".matches("\\w?"));
System.out.println("abc".matches("\\w?"));
System.out.println("abc12".matches("\\w*"));//*代表0次或多次
System.out.println("".matches("\\w*"));
System.out.println("abc12张".matches("\\w*"));
System.out.println("abc12".matches("\\w+"));//+ 代表1次或多次
System.out.println("".matches("\\w+")); // false
System.out.println("abc12张".matches("\\w+"));
System.out.println("a3c".matches("\\w{3}"));//{3}代表要正好是n次
System.out.println("abcd".matches("\\w{3}"));
System.out.println("ahcd".matches("\\w{3,} "));// {3,} 代表是>=3次
System.out.println("ab".matches("\\w{3,}"));
System.out.println("abcde徐".matches("\\w{3,}"));
System.out.println("abc232d".matches("\\w{3,9}"));
System.out.println("-----------------");
// 4、其他几个常用的符号:(?i)忽略大小写 , 或:| , 分组:()
System.out.println("abc".matches("(?i)abcy")); // true
System.out.println("ABc".matches("(?i)abc"));
System.out.println("aBc".matches("a((?i)b)c"));
System.out.println("ABc".matches("a((?i)b)c"));
//需求1:要求要么是3个小写字母,要么是3个数字
System.out.println("123".matches("\\d{3}|[a-z]{3}"));
System.out.println("abc".matches("\\d{3}|[a-z]{3}"));
System.out.println("aAc".matches("\\d{3}|[a-z]{3}"));
// 需求2:必须是"我爱”开头,中间可以是至少一个"编程”、最后至少是1个"666”(必须是三个6一块出现)
System.out.println("我爱编程编程666666".matches("我爱(编程)+(666)+"));
System.out.println("我爱编程编程6666666".matches("我爱(编程)+(666)+")); //false
}
}
爬取信息(应用案例)
核心代码(正则表达式):
String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|" +
"(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
邮箱的正则表达式:
"(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|"
电话号的正则表达式:
(1[3-9]\\d{9})
座机的正则表达式:
(0\d{2,5}-?\d{5,15})|400-?\d{3,8}-?\d{3,8}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test4 {
public static void main(String[] args) {
String data = "来学习Java,\n" +
"电话:18666678888,18699997777\n" +
"或者联系邮箱:boniu@itcast.cn,\n" +
"座机电话:01036517895,010-98951256\n" +
"邮箱:bozai@itcast.cn,\n" +
"邮箱2:dlei0009@163.com,\n" +
"热线电话:400-618-9090,400-618-4000,4006184000,4006189090";
//System.out.println(data);
//1.定义爬取规则(正则表达式)
String regex = "(\\w{1,}@\\w{2,10}(\\.\\w{2,10}){1,2})|" +
"(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15})|400-?\\d{3,8}-?\\d{3,8}";
//2.把正则表达式封装成一个Pattern对象
Pattern pattern=Pattern.compile(regex);
// 3.通过pattern对象得到查找内容的匹配器
Matcher matcher = pattern.matcher(data);
//4.通过匹配器开始去内容中查找信息
while(matcher.find()) {
System.out.println(matcher.group());//取出信息
}
}
}
异常
抛出异常(throws)
在方法上使用throws关键字,可以将方法内部出现的异常抛出去给调用者处理,
方法 throws 异常1,异常2 ,异常3 ..{
}
捕获异常(try...catch)
直接捕获程序出现的异常。
try{
// 监视可能出现异常的代码!
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}
自定义异常![](https://i-blog.csdnimg.cn/blog_migrate/1ae2736755980eb77939ffa6b95f47f4.png)
自定义运行异常
核心源码:
public class AgeIllegalRuntimeException extends RuntimeException {
public AgeIllegalRuntimeException() {
}
public AgeIllegalRuntimeException(String message) {
super(message);
}
}
运行结果:
源码~~(运行时异常):
public class Test5 {
public static void main(String[] args) {
try {
saveAge(160);
System.out.println("底层运行成功!!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("底层出现bug!!");
}
}
public static void saveAge(int age) {
if (age > 0 && age < 150) {
System.out.println("年龄保存成功: " + age);
} else {
//用一个异常对象封装这个问题
//throw 抛出去这个异常问题
throw new AgeIllegalRuntimeException("/age is illegal,your age is: " + age);
}
}
}
public class AgeIllegalRuntimeException extends RuntimeException {
public AgeIllegalRuntimeException() {
}
public AgeIllegalRuntimeException(String message) {
super(message);
}
}
自定义编译异常
核心代码:
public class AgeIllegalException extends Exception {
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
}
需要通过try catch进行捕捉
运行结果:
源码~~(编译时异常):
public class Test5 {
public static void main(String[] args) {
// try {
// saveAge(160);
// System.out.println("底层运行成功!!");
// } catch (Exception e) {
// e.printStackTrace();
// System.out.println("底层出现bug!!");
// }
// saveAge2(160);
// saveAge2(23);
try {
saveAge2(160);
System.out.println("底层运行成功!!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("底层出现bug!!");
}
}
public static void saveAge2(int age) throws AgeIllegalException{
if (age > 0 && age < 150) {
System.out.println("年龄保存成功: " + age);
} else {
//用一个异常对象封装这个问题
//throw 抛出去这个异常问题
//throws 用在方法上,抛出方法内部的异常
throw new AgeIllegalException("/age is illegal,your age is: " + age);
}
}
}
public class AgeIllegalException extends Exception {
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
}
异常处理
捕获异常,记录并响应合适的信息给用户
运行结果:
源码:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test6 {
public static void main(String[] args) {
try {
test1();
} catch (FileNotFoundException e) {
e.printStackTrace(); //打印这个异常对象的信息,记录下来。
} catch (ParseException e) {
e.printStackTrace();
}
}
public static void test1() throws FileNotFoundException, ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
Date d = sdf.parse("2024-05-01 10:10");
System.out.println(d);
test2();
}
public static void test2() throws FileNotFoundException {
InputStream is = new FileInputStream("D:/App.png");
}
}
注意一点(小优化):
所有的异常类的祖宗类都是Exception,所以可以直接抛给Exception
如下:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test6_2 {
public static void main(String[] args) {
try {
test1();
} catch (Exception e) {
e.printStackTrace(); //打印这个异常对象的信息,记录下来。
}
}
public static void test1() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss" );
Date d = sdf.parse("2024-05-01 10:10");
System.out.println(d);
test2();
}
public static void test2() throws Exception {
InputStream is = new FileInputStream("D:/App.png");
}
}
捕获异常,尝试重新修复
运行结果:
源码:
import java.util.Scanner;
public class Test7 {
public static void main(String[] args) {
while (true) {
try {
System.out.println(getMoney());
} catch (Exception e) {
System.out.println("请输入合适的数字!");
//e.printStackTrace();
}
}
}
public static double getMoney() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入合适价格: ");
double money = sc.nextDouble();
if (money >= 0)
return money;
else {
System.out.println("输入异常!!");
}
return 0;
}
}
集合
Collection
泛型类 , 接口
运行结果:
源码:
import java.util.ArrayList;
import java.util.HashSet;
public class Test1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("java1");
list.add("java2");
list.add("java1");
list.add("java2");
System.out.println(list);
HashSet<String> set =new HashSet<>();
set.add("java1");
set.add("java2");
set.add("java1");
set.add("java2");
set.add("java3");
System.out.println(set);
}
}
Collection常用方法
为啥要先学Collection的常用方法?
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class Test2 {
public static void main(String[] args) {
Collection c = new ArrayList<>();
c.add("java1");
c.add("java2");
c.add("java3");
c.add("java4");
//是否包含某个元素
System.out.println(c.contains("java"));
System.out.println(c.contains("java1"));
//删除元素
System.out.println(c.remove("java1")); //true
System.out.println(c);
//把集合转换成数组
//toArray()
Object[] arr = c.toArray();
System.out.println(Arrays.toString(arr));
//把一个集合的全部元素都倒入到另一个集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2);
System.out.println(c1);
System.out.println(c2);
}
}
运行结果:
源码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class Test2 {
public static void main(String[] args) {
Collection c = new ArrayList<>();
c.add("java1");
c.add("java2");
c.add("java3");
c.add("java4");
//是否包含某个元素
System.out.println(c.contains("java"));
System.out.println(c.contains("java1"));
//删除元素
System.out.println(c.remove("java1")); //true
System.out.println(c);
//把集合转换成数组
//toArray()
Object[] arr = c.toArray();
System.out.println(Arrays.toString(arr));
//把一个集合的全部元素都倒入到另一个集合中
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2);
System.out.println(c1);
System.out.println(c2);
}
}
Collection常用遍历
Iterator(迭代器)
运行结果:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test3 {
public static void main(String[] args) {
Collection<String> c = new ArrayList();
c.add("der");
c.add("ser");
c.add("cer");
c.add("xer");
System.out.println(c);
Iterator<String> i = c.iterator();
// System.out.println(i.next());
// System.out.println(i.next());
// System.out.println(i.next());
// System.out.println(i.next());
while (i.hasNext()){
String ele = i.next();
System.out.println(ele);
}
}
}
增强for循环
- 增强for可以用来遍历集合或者数组。
- 增强for遍历集合,本质就是迭代器遍历集合的简化写法。
核心代码:
for (String s : c) {
System.out.println(s);
}
运行结果:
Lambda表达式遍历集合
核心代码(含简化过程):
// c.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
//
// c.forEach((String s) -> {
// System.out.println(s);
// });
//
// c.forEach(s -> System.out.println(s));
c.forEach(System.out::println);
运行结果:
源码:
import java.util.ArrayList;
import java.util.Collection;
public class Test3 {
public static void main(String[] args) {
Collection<String> c = new ArrayList();
c.add("der");
c.add("ser");
c.add("cer");
c.add("xer");
System.out.println(c);
// c.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
//
// c.forEach((String s) -> {
// System.out.println(s);
// });
//
// c.forEach(s -> System.out.println(s));
c.forEach(System.out::println);
}
}
List
List特有方法
运行结果:
源码:
import java.util.ArrayList;
import java.util.List;
public class Test4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("der");
list.add("aer");
list.add("ser");
list.add("wer");
System.out.println(list);
//根据索引进行插入元素
list.add(2,"hhh");
System.out.println(list);
//根据索引进行删除元素,并返回删除元素
System.out.println(list.remove(2));
System.out.println(list);
//返回指定位置的元素
System.out.println(list.get(3));
//修改索引位置的元素,修改成功后,会返回原来的元素
System.out.println(list.set(3,"qqq"));
System.out.println(list);
}
}
List支持的遍历方式
- for循环(因为List集合有索引)
- 迭代器
- 增强for循环
- Lambda表达式
核心代码:
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("---------------");
Iterator it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("---------------");
for (String s : list) {
System.out.println(s);
}
System.out.println("---------------");
list.forEach(s-> System.out.println(s));
System.out.println("----------------------");
运行结果:
ArrayList
底层原理
基于数组实现
查询数据速度快(注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
删除效率低:可能需要把后面很多的数据进行前移
添加效率低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
LinkedList
基于双链表实现的
单链表特点:
- 查询慢,无论是查询哪个数据都要从头开始查询、
- 链表增删相对快
双链表特点:
查询慢,增删相对快,但是首位元素进行增删改查的速度是极快的
可以实现stack和quene
运行结果:
quene
核心代码:
LinkedList<String> quene = new LinkedList<>();
//入队
quene.addLast("1");
quene.addLast("2");
quene.addLast("3");
quene.addLast("4");
System.out.println(quene);
//出队
System.out.println(quene.removeFirst());
System.out.println(quene.removeFirst());
System.out.println(quene);
stack
可以直接使用push和pop
核心代码:
LinkedList<String> stack = new LinkedList<>();
//压栈
stack.push("1");
stack.addFirst("2");
stack.addFirst("3");
stack.addFirst("4");
System.out.println(stack);
//出栈
System.out.println(stack.pop());
System.out.println(stack.removeFirst());
System.out.println(stack);
set
HashSet
底层逻辑:
哈希值:
- 就是一个int类型的数值,Java中每个对象都有一个哈希值。
- Java中的所有对象,都可以调用0bejct类提供的hashcode方法,返回该对象自己的哈希值
对象哈希值的特点
- 同一个对象多次调用hashcode()方法返回的哈希值是相同的。
- 不同的对象,它们的哈希值一般不相同,"但也有可能会相同(哈希碰撞)。 (个例,很少很少)
基于哈希表实现
增删改查性能都很好的数据结构
哈希表 = 数组 + 链表 + 红黑树
加载因子是用来计算数组长度是否接近全满
当达到 x = 默认长度 * 加载因子,就会进行扩容
JDK8开始,当链表超过8,且数组长度>=64时,自动将链表转换成红黑树
核心代码(重写函数):
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
运行结果:
源码:
import java.util.Objects;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
public class Test3 {
public static void main(String[] args) {
Set<Student> studentSet = new HashSet<>();
Student s1 = new Student("der", 19);
Student s2 = new Student("der", 19);
Student s3 = new Student("wer", 19);
Student s4 = new Student("aer", 19);
studentSet.add(s1);
studentSet.add(s2);
studentSet.add(s3);
studentSet.add(s4);
System.out.println(studentSet);
}
}
public class Student {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
LinkedHashSet
通过占用内存换取有序
TreeSet![](https://i-blog.csdnimg.cn/blog_migrate/ad316cdf86eae5a4188356c0217a28a3.png)
需要自定义排序规则:
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则
方式一:
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
方式二:
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则
public TreeSet(Comparator<?super E>comparator)
注意一点:重写比较方法如果这样的话会认为数据相同,只保留一个。
只要让数据相同的不返回0即可。
@Override
public int compareTo(Student o) {
return this.age - o.age;
// if (this.age != o.age)
// return this.age - o.age;
// else
// return 1;
}
}
运行结果:
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
public class Test3 {
public static void main(String[] args) {
Set<Student> studentSet = new TreeSet<>();
Student s1 = new Student("der", 19);
Student s2 = new Student("der", 16);
Student s3 = new Student("wer", 18);
Student s4 = new Student("aer", 19);
studentSet.add(s1);
studentSet.add(s2);
studentSet.add(s3);
studentSet.add(s4);
System.out.println(studentSet);
}
}
public class Student implements Comparable<Student> {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// return this.age - o.age;
if (this.age != o.age)
return this.age - o.age;
else
return 1;
}
}
总结
- 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用) -
如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。 -
如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用Hashset集合(无序,不重复,无索引),底层基于哈希表实现的。(常用) -
如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashset集合(有序,不重复,无索引),底层基于哈希表和双链表。. -
如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用Treeset集合,基于红黑树实现。
集合的并发修改异常
- 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
- 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
修改异常的方法:
- 使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。
Iterator<String> it = list.iterator(); while (it.hasNext()) { String name = it.next(); if (name.contains("d")) { it.remove(); } } System.out.println(list);
- 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i--操作。
for (int i = 0; i < list.size(); i++) { String name = list.get(i); if(name.contains("d")){ list.remove(name); i--; } } System.out.println(list);
源码:
import java.util.ArrayList;
import java.util.Iterator;
public class Test4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("der");
list.add("der");
list.add("wer");
list.add("ser");
System.out.println(list);
System.out.println("---------------");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String name = it.next();
if (name.contains("d")) {
it.remove();
}
}
System.out.println(list);
for (int i = 0; i < list.size(); i++) {
String name = list.get(i);
if(name.contains("d")){
list.remove(name);
i--;
}
}
System.out.println(list);
}
}
可变参数
就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
int...nums
可变参数的特点和好处
特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它
好处:常常用来灵活的接收数据。
可变参数的注意事项:
- 可变参数在方法内部就是一个数组。
- 一个形参列表中可变参数只能有一个
- 可变参数必须放在形参列表的最后面
运行结果:
源码:
import set.Student;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
//批量添加元素
List<String> names = new ArrayList<>();
Collections.addAll(names,"der","ser","aer","wer");
System.out.println(names);
//打乱元素顺序
Collections.shuffle(names);
System.out.println(names);
//排序,升序
List<Student> students = new ArrayList<>();
Student s1 = new Student("der", 19);
Student s2 = new Student("der", 16);
Student s3 = new Student("wer", 18);
Student s4 = new Student("aer", 19);
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
Collections.sort(students, Comparator.comparingInt(Student::getAge));
System.out.println(students);
}
}
import java.util.Objects;
public class Student implements Comparable<Student> {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// return this.age - o.age;
if (this.age != o.age)
return this.age - o.age;
else
return 1;
}
}
Map
Map集合体系的特点
注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的
- HashMap(由键决定特点):无序、不重复、无索引: (用的最多)
- LinkedHashMap (由键决定特点):由键决定的特点:有序、不重复、无索引。
- TreeMap (由键决定特点):按照大小默认升序排序、不重复、无索引。
Map常用方法
运行结果(对比着源码看):
{der=60, java=30, phone=600}
--------------
3
false
--------------
600
null
--------------
600
{der=60, java=30}
--------------
false
true
false
--------------
true
false
--------------
[der, java]
--------------
[60, 30]
--------------
{der=60, java=30}
{der=10, ser=30, java=30, wer=20}
{der=10, ser=30, wer=20}
源码:
import java.util.*;
public class Test2 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("phone", 200);
map.put("phone", 600); //后面重复的数据会覆盖前面的数据(键)
map.put("der", 60);
map.put("java", 30);
System.out.println(map);
System.out.println("--------------");
System.out.println(map.size());
// map.clear();
// System.out.println(map);
System.out.println(map.isEmpty());
System.out.println("--------------");
//根据键获得相应值
System.out.println(map.get("phone"));
System.out.println(map.get("wer"));
System.out.println("--------------");
//根据键删除相应值,并返回相应值
System.out.println(map.remove("phone"));
System.out.println(map);
System.out.println("--------------");
//判断是否包含某个键,返回 T F
System.out.println(map.containsKey("phone"));
System.out.println(map.containsKey("der"));
System.out.println(map.containsKey("Der"));
System.out.println("--------------");
//判断是否包含某个值,返回 T F
System.out.println(map.containsValue(30));
System.out.println(map.containsValue(10));
System.out.println("--------------");
//获取Map集合中所有的键
Set<String> keys = map.keySet();
System.out.println(keys);
System.out.println("--------------");
//获取Map集合中所有的值
Collection<Integer> values = map.values();
System.out.println(values);
System.out.println("--------------");
//把其他Map集合的数据倒入到自己的集合中来
Map<String ,Integer > map2 = new HashMap<>();
map2.put("der",10);
map2.put("wer",20);
map2.put("ser",30);
System.out.println(map);
map.putAll(map2);
System.out.println(map);
System.out.println(map2);
}
}
Map遍历方式
- 键找值:先获取Map集合全部的键,再通过遍历键来找值
import java.util.HashMap; import java.util.Map; import java.util.Set; public class Test3 { public static void main(String[] args) { Map<String, Double> m = new HashMap<>(); m.put("der", 169.9); m.put("ser", 165.4); m.put("aer", 164.2); m.put("wer", 180.6); m.put("der", 183.0); System.out.println(m); Set<String> keys = m.keySet(); System.out.println(keys); for (String key : keys) { double value = m.get(key); System.out.println(key + "--->" + value); } } }
- 键值对:把“键值对“看成一个整体进行遍历(难度较大)
for (Map.Entry<String, Double> stringDoubleEntry : m.entrySet()) { String key = stringDoubleEntry.getKey(); double value = stringDoubleEntry.getValue(); System.out.println(key + "--->" + value); }
- Lambda:JDK 1.8开始之后的新技术(非常的简单) (化简之前的代码:)
m.forEach(new BiConsumer<String, Double>() { @Override public void accept(String s, Double aDouble) { System.out.println(s + "-->" + aDouble); } });
化简之后的代码:
m.forEach((k, v) -> System.out.println(k + "-->" + v));
Map案例
案例要求:
某个班级80名学生,现在需要组织秋游活动,班长提供了四个景点依次是(A、B、C、D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多。
案例分析:
- 将80个学生选择的数据拿到程序中去,[A,A,B,A,B,C,D,...].
- 准备一个Map集合用于存储统计的结果,Map<String,Integer>,键是景点,值代表投票数量。
- 遍历80个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该景点,不存在存入“景点=1.存在则其对应值+1,
import java.util.*;
public class MapDemo {
public static void main(String[] args) {
List<String> data = new ArrayList<>();
String[] selects = {"A", "B", "C", "D"};
Random r = new Random();
for (int i = 1; i <= 80; i++) {
int index = r.nextInt(4);
data.add(selects[index]);
}
System.out.println(data);
Map<String, Integer> result = new HashMap<>();
for (String s : data) {
if (result.containsKey(s)) {
result.put(s, result.get(s) + 1);
} else {
result.put(s, 1);
}
}
System.out.println(result);
}
}
HashMap
HashMap集合的底层原理
HashMap跟Hashset的底层原理是一模一样的,都是基于哈希表实现的。
实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
哈希表
- JDK8之前,哈希表=数组+链表
- JDK8开始,哈希表=数组+链表+红黑树
- 哈希表是一种增删改查数据,性能都较好的数据结构。
- 但是它是无序,不能重复,没有索引支持的(由键决定特点)
- HashMap的键依赖hashcode方法和equals方法保证键的唯
- 如果键存储的是自定义类型的对象,可以通过重写hashcode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的,
运行结果(不重写equals和hashCode):
运行结果(重写后):
源码(包含equals和hashCode,可进行自己调整)
package Map;
import java.util.Objects;
import java.util.HashMap;
import java.util.Map;
public class Test4 {
public static void main(String[] args) {
Map<Student,String>map = new HashMap<>();
map.put(new Student("der",19),"dr");
map.put(new Student("der",19),"sr");
map.put(new Student("aer",19),"ar");
map.put(new Student("wer",19),"wr");
System.out.println(map);
}
}
public class Student {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
LinkedHashMap
LinkedHashMap集合的原理
底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)
实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。
TreeMap
- 特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
- 原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
TreeMap集合同样也支持两种方式来指定排序规则
- 让类实现Comparable接口,重写比较规则。
- TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。
运行结果:
源码:
import java.util.Objects;
import java.util.Map;
import java.util.TreeMap;
public class Test4 {
public static void main(String[] args) {
Map<Student,String>map = new TreeMap<>();
map.put(new Student("der",19),"dr");
map.put(new Student("der",19),"sr");
map.put(new Student("aer",19),"ar");
map.put(new Student("wer",19),"wr");
System.out.println(map);
}
}
public class Student implements Comparable<Student> {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
if (this.age != o.age)
return this.age - o.age;
else
return 1;
}
}
集合的嵌套使用
运行结果:
源码:
import java.util.*;
public class Test5 {
public static void main(String[] args) {
Map<String, List<String>> map = new HashMap<>();
List<String> cities1 = new ArrayList<>();
Collections.addAll(cities1, "南京市", "扬州市", "苏州市", "无锡市", "常州市");
map.put("江苏省", cities1);
List<String> cities2 = new ArrayList<>();
Collections.addAll(cities2, "武汉市", "孝感市", "十堰市", "宜昌市", "鄂州市");
map.put("湖北省", cities2);
System.out.println(map);
List<String> cities = map.get("江苏省");
for (String city : cities) {
System.out.println(city);
}
map.forEach((p, c) -> System.out.println(p + "-->" + c));
}
}
Stream
Stream流程:
样例:
运行结果:
源码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
Collections.addAll(names, "der", "dwr", "dqr", "ser");
System.out.println(names);
List<String> list = names.stream().filter(s -> s.startsWith("d") && s.length() == 3).collect(Collectors.toList());
List<String> list1 = names.stream().filter(s -> s.startsWith("d")).filter(a -> a.length() == 3).collect(Collectors.toList());
System.out.println(list);
System.out.println(list1);
}
}
如何获取Stream流
List数据转Stream流
List<String> names = new ArrayList<>();
Collections.addAll(names, "der", "dwr", "dqr", "ser");
Stream<String> stream = names.stream();
Set数据转Stream流
Set<String> set = new HashSet<>();
Collections.addAll(set, "der", "dwr", "dqr", "ser");
Stream<String> stream1 = set.stream();
Map数据转Stream流
Map<String, Double> map = new HashMap<>();
map.put("ser", 165.4);
map.put("aer", 164.2);
map.put("wer", 180.6);
map.put("der", 183.0);
//单独转化key
Set<String> ks = map.keySet();
Stream<String> stream2 = ks.stream();
//单独转化value
Collection<Double> vs = map.values();
Stream stream3 = vs.stream();
//形成entry进行一并转化
Set<Map.Entry<String, Double>> entries = map.entrySet();
Stream<Map.Entry<String, Double>> kvs = entries.stream();
数组数据转Stream流
String[] names2 = {"der", "dwr", "dqr", "ser"};
Stream<String> s1 = Arrays.stream(names2);
Stream<String> s2 = Stream.of(names2);
常用方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
public class Test3 {
public static void main(String[] args) {
List<Double> scores = new ArrayList<>();
Collections.addAll(scores, 88.5, 96.3, 100.0, 20.3, 59.6, 46.3);
scores.stream().sorted().filter(a -> a >= 60).forEach(a -> System.out.println(a));
List<Student> students = new ArrayList<>();
Student s1 = new Student("der", 19);
Student s2 = new Student("der", 19);
Student s3 = new Student("aer", 23);
Student s4 = new Student("wer", 18);
Collections.addAll(students, s1, s2, s3, s4);
students.stream().forEach(s -> System.out.println(s));
System.out.println("---------------------");
students.stream()
.filter(s -> s.getAge() >= 17 && s.getAge() <= 30)
.sorted((Comparator.comparingInt(Student::getAge)))
.forEach(s -> System.out.println(s));
System.out.println("---------------------");
students.stream()
.filter(s -> s.getAge() >= 17 && s.getAge() <= 30)
.sorted((Comparator.comparingInt(Student::getAge)))
.limit(1) //取前几个数据
.forEach(s -> System.out.println(s));
System.out.println("-------------------");
students.stream()
.filter(s -> s.getAge() >= 17 && s.getAge() <= 30)
.sorted((Comparator.comparingInt(Student::getAge)))
.skip(1) //跳过几个数据
.forEach(s -> System.out.println(s));
System.out.println("--------------------");
students.stream().map(Student::getName)
.distinct() //去重
.forEach(s -> System.out.println(s));
System.out.println("--------------------");
students.stream().distinct() //去重 ,因为地址不同,所以不能去重,需要重写equals方法和hashcode方法
.forEach(s -> System.out.println(s));
System.out.println("----------------------");
//合并a和b两个流为一个流
Stream<String> stream = Stream.of("der", "ser");
Stream<String> stream1 = Stream.of("aer", "wer");
Stream<String> concat = Stream.concat(stream1, stream);
concat.forEach(s-> System.out.println(s));
}
}
import java.util.Objects;
public class Student implements Comparable<Student> {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
if (this.age != o.age)
return this.age - o.age;
else
return 1;
}
}
运行结果:
88.5
96.3
100.0
Student{name='der', age=19}
Student{name='der', age=19}
Student{name='aer', age=23}
Student{name='wer', age=18}
---------------------
Student{name='wer', age=18}
Student{name='der', age=19}
Student{name='der', age=19}
Student{name='aer', age=23}
---------------------
Student{name='wer', age=18}
-------------------
Student{name='der', age=19}
Student{name='der', age=19}
Student{name='aer', age=23}
--------------------
der
aer
wer
--------------------
Student{name='der', age=19}
Student{name='aer', age=23}
Student{name='wer', age=18}
aer
wer
der
ser
Stream流终结方法
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Test4 {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
Student s1 = new Student("der", 19);
Student s2 = new Student("der", 19);
Student s3 = new Student("aer", 23);
Student s4 = new Student("wer", 18);
Collections.addAll(students, s1, s2, s3, s4);
long l = students.stream().filter(s -> s.getAge() >= 18).count();
System.out.println(l);
Student ss = students.stream().max(((o1, o2) -> o1.getAge() - o2.getAge())).get();
System.out.println(ss);
System.out.println("--------------");
//注意流只能收集一次
Stream<Student> studentStream = students.stream().filter(s -> s.getAge() >= 18);
// List<Student> students1 = studentStream.collect(Collectors.toList());
// System.out.println(students1);
// Set<Student> students2 = studentStream.collect(Collectors.toSet());
// System.out.println(students2);
System.out.println("--------------");
//每次重新一个新流就不会报错
List<Student> student1 = students.stream().filter(s->s.getAge()>=18).collect(Collectors.toList());
System.out.println(student1);
System.out.println("--------------");
List<Student> student2 = students.stream().filter(s->s.getAge()>=18).collect(Collectors.toList());
System.out.println(student2);
System.out.println("-------------------");
Map<String,Integer>map = students.stream()
.filter(s->s.getAge()>=18).distinct()
.collect(Collectors.toMap(a->a.getName(),a->a.getAge()));
System.out.println(map);
System.out.println("-------------------");
Student[] students1 = students.stream().toArray(len->new Student[len]);
System.out.println(Arrays.toString(students1));
System.out.println("-------------------");
}
}
import java.util.Objects;
public class Student implements Comparable<Student> {
private String name;
private int 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 Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Student o) {
if (this.age != o.age)
return this.age - o.age;
else
return 1;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public 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);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
运行结果:
4
Student{name='aer', age=23}
--------------
--------------
[Student{name='der', age=19}, Student{name='der', age=19}, Student{name='aer', age=23}, Student{name='wer', age=18}]
--------------
[Student{name='der', age=19}, Student{name='der', age=19}, Student{name='aer', age=23}, Student{name='wer', age=18}]
-------------------
{der=19, wer=18, aer=23}
-------------------
[Student{name='der', age=19}, Student{name='der', age=19}, Student{name='aer', age=23}, Student{name='wer', age=18}]
-------------------
文件存储
File
运行结果:
源码:
import java.io.File;
public class Test {
public static void main(String[] args) {
// File f1 = new File("D:/resource/ab.txt");
// File f1 = new File("D:\\resourcellab.txt");
File f1 = new File( "D:"+ File.separator +"resource" + File.separator + "ab.txt");
System.out.println(f1.length());
File f2 = new File( "D:/resource");
System.out.println(f2.length());
// 注意:File对象可以指代一个不存在的文件路径
File f3 = new File( "D:/resource/aaaa.txt");
System.out.println(f3.length());
System.out.println(f3.exists());
//我现在要定位的文件是在模块中,应该怎么定位呢?
// 绝对路径:带盘符的
// File f4 = new File("D:\\code\\javasepromax\\file-io-app\\src\\der.txt");
// 相对路径(重点):不带盘符,默认是直接去工程下寻找文件的。
File f4 = new File( "../Java_studyself\\opp-app5\\src\\File\\der.txt");
System.out.println(f4.length());
}
}
常用方法:
运行结果:
源码:
import java.io.File;
import java.text.SimpleDateFormat;
public class Test2 {
public static void main(String[] args) {
//1.创建文件对象,指代某个文件
File f1 = new File("D:\\study\\JAVA\\file\\der.txt");
//File f1 =new File("D:/study/JAVA/file/der.txt");
//2、public boolean exists();
// 判断当前文件对象,对应的文件路径是否存在,存在返回true
System.out.println(f1.exists());
//3、public boolean isFile():判断当前文件对象指代的是否是文件,是文件返回true,反之。
System.out.println(f1.isFile());
//4、public boolean isDirectory():判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
System.out.println(f1.isDirectory());
//5.public string getName():获取文件的名称(包含后缀)
System.out.println(f1.getName());
//6.public long length():获取文件的大小,返回字节个数
System.out.println(f1.length());
//7.public long lastModified():获取文件的最后修改时间。
long time = f1.lastModified();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(sdf.format(time));
//8.public string getPath():获取创建文件对象时,使用的路径
File f2 = new File("D:\\study\\JAVA\\project\\Java_studyself\\opp-app5\\src\\File\\der.txt");
File f3 = new File("../Java_studyself\\opp-app5\\src\\File\\der.txt");
System.out.println(f2.getPath());
System.out.println(f3.getPath());
//9.public string getAbsolutePath():获取绝对路径
System.out.println(f2.getAbsolutePath());
System.out.println(f3.getAbsolutePath());
}
}
文件的建立与删除
注意!!!!!这里的删除是直接删除,不进入回收站
运行结果:
源码:
import java.io.File;
import java.io.IOException;
public class Test3 {
public static void main(String[] args) throws IOException {
File f1 = new File("D:/F");
System.out.println(f1.mkdir());
File f2 = new File("D:/F/ab.txt");
System.out.println(f2.createNewFile());
File f3 = new File("D:/F/aa/bb/cc/dd");
System.out.println(f3.mkdirs());
System.out.println(f2.delete());
File f4 = new File("D:/F/aa/bb/cc/dd");
System.out.println(f4.delete());
}
}
遍历方法
String[] names = f1.list();
for(String name :names){
System.out.println(name);
}
File[] files = f1.listFiles();
for(File file :files){
System.out.println(file.getAbsolutePath());
}
案例应用
import java.io.File;
public class Demo {
public static void main(String[] args) {
searchFile(new File("D:"), "der.txt");
}
/**
* 目录下搜索文件
*
* @param dir 目录
* @param fileName 要搜索的文件名称
*/
public static void searchFile(File dir, String fileName) {
//1.先把非法的情况拦住
if (dir == null || !dir.exists() || dir.isFile()) {
return;
}
//2.dir不是null,一定是目录对象
//获取当前目录下全部的一级文件对象
File[] files = dir.listFiles();
//3.判断当前目录是否存在一级文件对象,以及能否可以拿到一级文件对象
if (files != null && files.length > 0) {
for (File f : files) {
if (f.isFile()) {
if (f.getName().contains(fileName)) {
System.out.println("找到了:" + f.getAbsolutePath());
} else {
searchFile(f, fileName);
}
}
}
}
}
}
删除文件夹(非空)
仅提供方法,不建议尝试,不能找回!!!
public static void deleteDir(File dir) {
if (dir == null || !dir.exists()) {
return;
}
if (dir.isFile()) {
dir.delete();
return;
}
// 1、dir存在且是文件夹。拿里面的一级文件对象
File[] files = dir.listFiles();
if (files == null) {
return;
}
// 2、这是一个有内容的文件夹干掉里面的内容,再干掉自己、
for (File file : files) {
if (file.isFile()) {
file.delete();
} else {
deleteDir(file);
}
}
dir.delete();
}
字符集
运行结果:
源码:
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Test4 {
public static void main(String[] args) throws UnsupportedEncodingException {
// 1、编码
String data = "der你好";
byte[] bytes = data.getBytes();// 默认是按照平台字符集(UTF-8)进行编码的。
System.out.println(Arrays.toString(bytes));
// 按照指定字符集进行编码。
byte[] bytes1 = data.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
// 2、解码
String s1 = new String(bytes);// 按照平台默认编码(UTF-8)解码
System.out.println(s1);
String s2 = new String(bytes1, "GBK");
System.out.println(s2);
}
}
IO流
总结流的四大类:
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。
字节流
字节输入流
每次读取一个字节
注意事项:
- 读取数据性能差
- 汉字读取是乱码!无法避免!!
- 流使用完毕之后,必须关闭!释放系统资源
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Test1 {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("../Java_studyself\\IO\\src\\IO1\\dder");
// int b1 = is.read();
// System.out.println(b1);
// System.out.println((char) b1);
//
// int b2 = is.read();
// System.out.println(b2);
// System.out.println((char) b2);
//
// int b3 = is.read();
// System.out.println(b3);
// System.out.println((char) b3);
int b ;
while ((b = is.read())!= -1){
System.out.print((char) b);
}
System.out.println();
is.close();
//读取数据性能差
//汉字读取是乱码!无法避免!!
//流使用完毕之后,必须关闭!释放系统资源
}
}
每次读取多个字节
运行结果:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Test2 {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("../Java_studyself\\IO\\src\\IO1\\dder1");
//
// byte[] buffer = new byte[3];
// int len = is.read(buffer);
// String rs = new String(buffer);
// System.out.println(rs);
// System.out.println("当次读取字节数量:" + len);
//
因为没有多余的数据对“c”进行覆盖,所以c不会消失,而a b 是因为6 6 覆盖所以没有;
int len2 = is.read(buffer);
String rs2 = new String(buffer);
System.out.println(rs2);
System.out.println("当次读取字节数量:" + len2);
//
// //解决方法
// int len2 = is.read(buffer);
// String rs2 = new String(buffer, 0, len2);
// System.out.println(rs2);
// System.out.println("当次读取字节数量:" + len2);
//
// int len3 = is.read(buffer);
// System.out.println(len3);
//注意:读取多少倒多少
//使用循环进行改造
byte[] b = new byte[3];
int len;
while ((len = is.read(b)) != -1) {
String rs = new String(b, 0, len);
System.out.print(rs);
}
System.out.println();
//但是文字会出现乱码
is.close();
}
}
一次性直接读取全部字节
直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?
如果文件过大,创建的字节数组也会过大,可能引起内存溢出,
读写文本内容更适合用:字符流
字节流适合做数据的转移,如:文件复制等
运行结果:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class Test3 {
public static void main(String[] args) throws IOException {
//一次性读取文件的全部字节放到一个数组中
InputStream is = new FileInputStream("../Java_studyself\\IO\\src\\IO1\\dder2");
//准备一个字节数组,大小刚好与文件的大小一样
// File f = new File("../Java_studyself\\IO\\src\\IO1\\dder2");
// long size = f.length();
// byte[] b = new byte[(int) size];
//
// int len = is.read(b);
// System.out.println(size);
// System.out.println(len);
byte[] b = is.readAllBytes();
System.out.println(new String(b));
}
}
字节输出流
运行结果:
源码:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Test {
public static void main(String[] args) throws IOException {
//创立一个字节输出流
//覆盖管道
// OutputStream os = new FileOutputStream("../Java_studyself\\IO\\src\\IO2\\dder.txt");
//追加数据管道
OutputStream os = new FileOutputStream("../Java_studyself\\IO\\src\\IO2\\dder.txt",true);
//写入字节数据
os.write(97);
os.write('b');
// os.write('哈'); //默认只能加入一个字节
byte[] bytes = "我爱中国abc".getBytes();
os.write(bytes);
os.write("\r\n".getBytes()); //换行
os.close();
}
}
运行结果:
源码:
import java.io.*;
public class Test2_CpoyPhoto {
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("D:\\study\\JAVA\\project\\Java_studyself\\IO\\src\\IO2\\background.png");
OutputStream os = new FileOutputStream("D:/background.png");
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
os.close();
is.close();
System.out.println("复制成功");
}
}
try-catch-finally释放资源
使用场景:
在finally直接释放系统资源
无论是否出现问题都会执行finally语句
运行结果:
即使在语句中加入return语句也会执行finally语句,只有一种情况不会执行,那就是直接通过关闭虚拟机关闭才能避免使用
System.exit(0);
public class Test4 {
public static void main(String[] args) {
try {
System.out.println(10 / 2);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("----finally执行一次-----");
}
// try {
// System.out.println(10 / 0);
// } catch (Exception e) {
// e.printStackTrace();
// } finally {
// System.out.println("----finally执行一次-----");
// }
}
}
!!!千万不能在finally返回数据!!!
运行结果:
实例展示
import java.io.*;
public class Test2_CpoyPhoto {
public static void main(String[] args) {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("D:\\study\\JAVA\\project\\Java_studyself\\IO\\src\\IO2\\background.png");
os = new FileOutputStream("D:/background.png");
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源的操作
try {
if (os != null) os.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (os != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("复制成功");
}
}
try-catch-resource释放资源
在括号里只能放资源,不能放变量
import java.io.*;
public class Test3 {
public static void main(String[] args) {
try (
InputStream is = new FileInputStream("D:\\study\\JAVA\\project\\Java_studyself\\IO\\src\\IO2\\background.png");
OutputStream os = new FileOutputStream("D:/background.png");
){
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("复制成功");
}
}
字符流
字符输入流
可以通过进行单个字符的输入,也可以类似于字节输入通过字符数组进行一块输入
运行结果:
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Test {
public static void main(String[] args) {
try (
Reader r = new FileReader("../Java_studyself\\IO\\src\\IO3\\dder.txt");
) {
// int c;
// while ((c = r.read()) != -1) {
// System.out.print((char) c);
// }
// System.out.println();
char[] br = new char[3];
int len;
while ((len = r.read(br)) != -1) {
System.out.print(new String(br, 0, len));
}
System.out.println();
//读取多个字符性能不错
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符输出流
运行结果:
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Test2 {
public static void main(String[] args) {
try (
//覆盖管道
// Writer f = new FileWriter("../Java_studyself\\IO\\src\\IO3\\dder1.txt")
//追加管道
Writer f = new FileWriter("../Java_studyself\\IO\\src\\IO3\\dder2.txt",true)
) {
// 1、public void write(int c):写一个字符出去
f.write('a');
f.write(97);
f.write('啊');
// 2、public void write(string c)写一个字符串出去
f.write("我爱你中国abc\r\n");
// 3、public void write(string c,int pos ,int len):写字符串的一部分出去
f.write( "我爱你中国abc\r\n",0, 5);
//4、public void write(char[] buffer):写一个字符数组出去
char[] buffer = {'哈','楽','d','e','r'};
f.write(buffer);
// 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
f.write(buffer,0,2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符输出流使用时的注意事项
字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效
f.flush(); //流的刷新,流刷新之后还可以继续使用
f.close(); //流的关闭,流关闭之后就不可以继续使用啦,但是流的关闭包括流的刷新
数据先放到缓冲区,然后等到刷新直接才放到文件之中,如果缓冲区装满了,数据不会丢失,而是先放到文档之中去,然后继续缓冲。
字节流、字符流的使用场景小结
- 字节流适合做一切文件数据的拷贝(音视频,文本);
- 字节流不适合读取中文内容输出字符流适合做文本文件的操作(读,写)。
缓冲流
字节缓冲输入流/字节缓冲输出流
import java.io.*;
public class Test3 {
public static void main(String[] args) {
try (
InputStream is = new FileInputStream("D:\\study\\JAVA\\project\\Java_studyself\\IO\\src\\IO2\\background.png");
OutputStream os = new FileOutputStream("D:/background.png");
){
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("复制成功");
}
}
字符缓冲输入流
作用:自带8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能。
注意因为要调用,BufferedReader的一个特殊换行功能,就不能使用多态去写
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Test4 {
public static void main(String[] args) {
try (
Reader r = new FileReader("../Java_studyself\\IO\\src\\IO3\\dder2.txt");
BufferedReader br = new BufferedReader(r);
) {
// char[] buffer = new char[3];
// int len;
// while ((len = br.read(buffer)) != -1) {
// System.out.println(new String(buffer, 0, len));
// }
String line ;
while ((line = br.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符缓冲输出流
作用:自带8K的字符缓冲池,可以提高字符输出流写字符数据的性能。
注意因为要调用,BufferedReader的一个特殊换行功能,就不能使用多态去写
运行结果:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Test5 {
public static void main(String[] args) {
try (
Writer f = new FileWriter("../Java_studyself\\IO\\src\\IO3\\dder3.txt",true);
BufferedWriter bw = new BufferedWriter(f);
) {
bw.write('a');
bw.write(97);
bw.write('啊');
bw.newLine();
bw.write("我爱你中国abc\r\n");
bw.write( "我爱你中国abc\r\n",0, 5);
char[] buffer = {'哈','楽','d','e','r'};
bw.write(buffer);
bw.write(buffer,0,2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
案例展示(文件内容排序,并复制到新文件之中)
运用到缓冲字符输出流,缓冲字符输入流
运行结果:
源码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test6 {
public static void main(String[] args) {
try (
//2.创建缓冲字符流与源文件建立联系
BufferedReader br = new BufferedReader(new FileReader("../Java_studyself\\IO\\src\\IO3\\csb.txt"));
//5.创立缓冲字符流与新文件建立联系
BufferedWriter bw = new BufferedWriter(new FileWriter("../Java_studyself\\IO\\src\\IO3\\out.txt"));
){
//1.先定义一个ArraysList集合储存每段文字
List<String> list = new ArrayList<>();
//3.按照行读取每段文字,存入集合中
String line ;
while ((line = br.readLine()) != null) {
list.add(line);
}
//4.对集合进行排序
Collections.sort(list);
System.out.println(list);
//5.一次遍历List每段文字,依次写入新文件中
for(String ln :list){
bw.write(ln);
bw.newLine(); //换行
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
原始流、缓冲流的性能分析[重点]
测试用例:
分别使用原始的字节流,以及字节缓冲流复制一个很大视频。
测试步骤
- 使用低级的字节流按照一个一个字节的形式复制文件。
- 使用低级的字节流按照字节数组的形式复制文件,
- 使用高级的缓冲字节流按照一个一个字节的形式复制文件,
- 使用高级的缓冲字节流按照字节数组的形式复制文件
运行结果:
源码:
package IO3;
import java.io.*;
public class TimeTest {
//定义复制视频路径
private static final String SRC_FILE = "D:\\study\\JAVA\\project\\Java_studyself\\IO\\src\\IO3\\《憨豆的黄金周》HD中字在线播放-茶杯狐.ts";
//定义复制到的位置
private static final String DEST_FILE = "D:\\";
public static void main(String[] args) {
// copy1(); //低级字节流一个一个字节复制,十分慢
copy2(); //低级字节流使用字节数组复制
copy3(); //缓冲流一个一个字节复制
copy4(); //缓冲流使用字节数组复制
}
public static void copy1() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
OutputStream os = new FileOutputStream(DEST_FILE + "1.ts");
) {
int b;
while ((b = is.read()) != -1) {
os.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
public static void copy2() {
long startTime = System.currentTimeMillis();
try (
InputStream is = new FileInputStream(SRC_FILE);
OutputStream os = new FileOutputStream(DEST_FILE + "2.ts");
) {
int len;
byte[] b = new byte[1024 * 8];
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
public static void copy3() {
long startTime = System.currentTimeMillis();
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SRC_FILE)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(DEST_FILE + "3.ts"));
) {
int b;
while ((b = bis.read()) != -1) {
bos.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
public static void copy4() {
long startTime = System.currentTimeMillis();
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(SRC_FILE)) ;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(DEST_FILE + "4.ts"));
) {
byte[] b = new byte[1024 * 8];
int len ;
while ((len = bis.read(b)) != -1) {
bos.write(b);
}
} catch (IOException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
}
}
不同编码读取出现乱码的问题
如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码!
如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件时就会出现乱码!
字符输入转化流
运行结果:
源码:
import java.io.*;
public class Test_chargeInput {
public static void main(String[] args) {
try (
//1.获取源文件的原始字节流(GBK字节流形式)
InputStream is = new FileInputStream("../Java_studyself\\IO\\src\\IO3\\dder3.txt");
//2.把原始的字节输入流按照指定的字符集编码转换成字符输入流
Reader ris = new InputStreamReader(is,"GBK");
//3.把字符输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(ris);
){
String line ;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
字符转化输出流
运行结果:
源码:
import java.io.*;
public class Test_chargeOutput {
public static void main(String[] args) {
try (
OutputStream os = new FileOutputStream("../Java_studyself\\IO\\src\\IO3\\dder4.txt");
Writer wos = new OutputStreamWriter(os,"GBK");
BufferedWriter bw = new BufferedWriter(wos);
){
bw.write("啊啊啊啊啊");
bw.newLine();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印流
PrintStream/PrintWriter(打印流)
作用:打印流可以实现更方便、更高效的打印数据出去,,能实现打印啥出去就是啥出去
PrintStream可以写字节,PrintWrite可以写字符
PrintWriter pw2 = new PrintWriter(new FileWriter("../Java_studyself\\IO\\src\\IO3\\dder5.txt",true));
这两个流不可以直接加上一个true进行追加数据
解决方法:
PrintWriter pw2 = new PrintWriter(new FileWriter("../Java_studyself\\IO\\src\\IO3\\dder5.txt",true));
输出语句的重定向
运行结果:
import java.io.PrintStream;
public class PrintTest2 {
public static void main(String[] args) {
System.out.println("aaaa");
System.out.println("bbbb");
try (
PrintStream ps = new PrintStream("../Java_studyself\\\\IO\\\\src\\\\IO3\\\\dder6.txt")
){
System.setOut(ps);
System.out.println("cccc");
System.out.println("dddd");
} catch (Exception e) {
e.printStackTrace();
}
}
}
数据输出流/数据输入流
package IO3.data_Stream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataOut {
public static void main(String[] args) {
try (
DataOutputStream dos =
new DataOutputStream(new FileOutputStream("../Java_studyself\\\\IO\\\\src\\\\IO3\\\\dder7.txt"))
) {
dos.writeInt(97);
dos.writeDouble(9.5);
dos.writeBoolean(true);
dos.writeUTF("哈哈哈");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package IO3.data_Stream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class DataInput {
public static void main(String[] args) {
try (
DataInputStream dis =
new DataInputStream(new FileInputStream("../Java_studyself\\\\IO\\\\src\\\\IO3\\\\dder7.txt"))
) {
int i = dis.readInt();
System.out.println(i);
double d = dis.readDouble();
System.out.println(d);
boolean b = dis.readBoolean();
System.out.println(b);
String s = dis.readUTF();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
对象字节输出流/对象字节输入流
运行结果:
package IO4;
import IO4.object_Stream.User;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class objectOut {
public static void main(String[] args) {
try (
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("../Java_studyself\\IO\\src\\IO4\\object_Stream\\der.txt"));
) {
User u = new User("rder", "der", 19, "666888999");
oos.writeObject(u);
System.out.println("序列化对象成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package IO4.object_Stream;
import java.io.Serializable;
public class User implements Serializable {
private String loginName;
private String userName;
private int age;
//transient 这个成员将不会参与进行序列化
private transient String password;
public User() {
}
public User(String loginName, String userName, int age, String password) {
this.loginName = loginName;
this.userName = userName;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"loginName='" + loginName + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", password='" + password + '\'' +
'}';
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package IO4.object_Stream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class objectInput {
public static void main(String[] args) {
User u = new User("rder", "der", 19, "666888999");
try (
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("../Java_studyself\\IO\\src\\IO4\\object_Stream\\der.txt"))
) {
Object o = ois.readObject();
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package IO4.object_Stream;
import java.io.Serializable;
public class User implements Serializable {
private String loginName;
private String userName;
private int age;
//transient 这个成员将不会参与进行序列化
private transient String password;
public User() {
}
public User(String loginName, String userName, int age, String password) {
this.loginName = loginName;
this.userName = userName;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"loginName='" + loginName + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", password='" + password + '\'' +
'}';
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
IO框架
运行结果:
特殊文件
properties文件
- 只能是键值对
- 、键不能重复
- 文件后缀一般是.properties结尾的
package properties_d1;
import java.io.FileReader;
import java.util.Properties;
import java.util.Set;
public class test {
public static void main(String[] args) throws Exception {
//1、创建一个Properties的对象出来(键值对集合,空容器)
Properties properties = new Properties();
System.out.println(properties);
//2、开始加载属性文件中的键值对数据到properties对象中去
properties.load(new FileReader("../Java_studyself\\file\\src\\properties_d1\\user.properties"));
System.out.println(properties);
// 3、根据键取值
System.out.println(properties.getProperty("der"));
System.out.println(properties.getProperty("asd"));
// 4、遍历全部的键和值。
Set<String> keys = properties.stringPropertyNames();
for(String key :keys) {
String value = properties.getProperty(key);
System.out.println(key + "---->" + value);
}
properties.forEach((k,v) -> {
System.out.println(k+"-->"+v);
});
}
}
package properties_d1;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class Test2 {
public static void main(String[] args) throws IOException {
//1、创建Properties对象出来,先用它存一些键值对数据
Properties properties = new Properties();
properties.setProperty("张无忌","minmin");
properties.setProperty("殷素素","cuishan");
properties.setProperty("张翠山","susu");
// 2.把properties对象中的键值对数据存入到属性文件中去
properties.store(new FileWriter("../Java_studyself\\file\\src\\properties_d1\\user2.properties"),
"i saved many users!");
}
}
XML文件
日志技术
- 可以将系统执行的信息,方便的记录到指定的位置(控制台、文件中、数据库中)
- 可以随时以开关的形式控制日志的启停,无需侵入到源代码中去进行修改
多线程
如何在程序中创建出多条线程?
Java是通过java.lang.Thread 类的对象来代表线程的
多线程的创建方式一:继承Thread类
- 定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
- 创建MyThread类的对象
- 调用线程对象的start()方法启动线程(启动后还是执行run方法的)
方式一优缺点:
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展,
package Thread;
public class ThreadTest {
//main方法是由一条默认的主线程负责执行的
public static void main(String[] args) {
//3、创建一MyThread线程类的对象代表一个线程
Thread t = new MyThread();
//4、启动线程
t.start(); //main是主线程 t是子线程
for (int i = 1; i <= 5; i++) {
System.out.println("主线程main输出:" + i);
}
}
}
package Thread;
/*
1、子类继承Thread
*/
public class MyThread extends Thread {
//2、重写run方法
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Mythread子线程输出: " + i);
}
}
}
package Thread;
public class runnableTeat {
public static void main(String[] args) {
Runnable target = new MyRunnable();
new Thread(target).start();
for (int i = 1; i <= 5; i++) {
System.out.println("主线程输出:" + i);
}
}
}
package Thread;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("子线程输出:" + i);
}
}
}
可以用Lambda进行简化
package Thread;
public class runnableTeat2 {
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("子线程1:"+i);
}
}
};
new Thread(r).start();
new Thread(() -> {
for (int i = 1; i <= 5; i++) {
System.out.println("子线程2:"+i);
}
}).start();
for (int i = 1; i <= 5; i++) {
System.out.println("main:"+i);
}
}
}
package Thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> call = new MyCallable(100);
FutureTask<String> f1 = new FutureTask<>(call);
new Thread(f1).start();
String rs = f1.get();
System.out.println(rs);
}
}
package Thread;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
//描述线程任务和返回结果
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return "线程求出了1-" + n + "的和是" + sum;
}
}
多线程方法
package Thread;
public class ThreadTest {
//main方法是由一条默认的主线程负责执行的
public static void main(String[] args) {
//3、创建一MyThread线程类的对象代表一个线程
// Thread t1 = new MyThread();
// t1.setName("1号线程"); //命名线程名字
Thread t1 = new MyThread("1号线程");
//4、启动线程
t1.start(); //main是主线程 t是子线程
System.out.println(t1.getName()); //Thread-0
// 3、创建一MyThread线程类的对象代表一个线程
Thread t2 = new MyThread("2号线程");
// t2.setName("2号线程");
//4、启动线程
t2.start(); //main是主线程 t是子线程
System.out.println(t2.getName()); //Thread-1 getName 获得名字
//主线程对象的名字
//currentThread 哪个线程执行,它就会获得哪个线程对象
Thread m = Thread.currentThread();
System.out.println(m.getName()); //main
for (int i = 1; i <= 5; i++) {
System.out.println("主线程main输出:" + i);
}
}
}
package Thread;
/*
1、子类继承Thread
*/
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
//2、重写run方法
@Override
public void run() {
Thread m = Thread.currentThread();
for (int i = 1; i <= 5; i++) {
System.out.println(m.getName() + "子线程输出: " + i);
}
}
}
线程安全
安全问题实例:
package Demo1;
public class Test {
public static void main(String[] args) {
Account acc =new Account(100000,"ICBC-111");
new DrawThread(acc,"wer").start(); //1
new DrawThread(acc,"der").start(); //2
}
}
package Demo1;
public class DrawThread extends Thread {
private Account acc;
private String name;
public DrawThread(Account acc, String name) {
super(name);
this.acc = acc;
}
@Override
public void run() {
acc.drawmoney(100000);
}
}
package Demo1;
public class Account {
private double money;
private String cardID;
public Account(double money, String cardID) {
this.money = money;
this.cardID = cardID;
}
public Account() {
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getCardID() {
return cardID;
}
public void setCardID(String cardID) {
this.cardID = cardID;
}
public void drawmoney(double money) {
//谁来取钱 1 和 2 同时来取钱
String name = Thread.currentThread().getName();
if (this.money >= money) {
System.out.println(name + "取钱:" + money + "元,成功!");
this.money -= money;
System.out.println(name + "取钱后余额为" + this.money + "元");
} else {
System.out.println(name + "取钱失败,余额不足");
}
}
}
线程同步(解决线程安全的方法)
![](https://i-blog.csdnimg.cn/blog_migrate/d921a09cd13be579aac953ba1bd4d915.png)
同步代码块
public void drawmoney(double money) {
//谁来取钱 1 和 2 同时来取钱
String name = Thread.currentThread().getName();
//通常使用this作为锁进行上锁,防止其他账户的用户
synchronized (this) {
if (this.money >= money) {
System.out.println(name + "取钱:" + money + "元,成功!");
this.money -= money;
System.out.println(name + "取钱后余额为" + this.money + "元");
} else {
System.out.println(name + "取钱失败,余额不足");
}
}
}
静态方法使用字节码对象作为锁,保证每次只有一个线程进入。
同步方法
public synchronized void drawmoney(double money) {
//谁来取钱 1 和 2 同时来取钱
String name = Thread.currentThread().getName();
if (this.money >= money) {
System.out.println(name + "取钱:" + money + "元,成功!");
this.money -= money;
System.out.println(name + "取钱后余额为" + this.money + "元");
} else {
System.out.println(name + "取钱失败,余额不足");
}
}
内含隐藏锁 (this)
Lock锁
注意要使用try catch finally 避免上锁后中间代码出bug,进而没有开锁
private final Lock lk = new ReentrantLock();
public void drawmoney(double money) {
//谁来取钱 1 和 2 同时来取钱
String name = Thread.currentThread().getName();
try {
lk.lock();
if (this.money >= money) {
System.out.println(name + "取钱:" + money + "元,成功!");
this.money -= money;
System.out.println(name + "取钱后余额为" + this.money + "元");
} else {
System.out.println(name + "取钱失败,余额不足");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lk.unlock();
}
}
乐观锁与悲观锁
乐观锁就是持比较乐观态度的锁。在操作数据时非常乐观,认为别的线程不会同时修改数据,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。一般使用CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
比较悲观的锁,总是想着最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。在Java中,synchronized从偏向锁、轻量级锁到重量级锁,全是悲观锁。JDK提供的Lock实现类全是悲观锁。一般用于多写的场景。
线程通信
线程池
创建线程池
线程池的注意事项:
临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会开始拒绝新任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.DAYS.SECONDS,
new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
运行 Runnable任务(无返回结果)![](https://i-blog.csdnimg.cn/blog_migrate/a2342b4d062b0150f77fb84a9199bbe1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5c854687c2402cba978525e3da676bbd.png)
使用CallerRunsPolicy(第四个),直接让主线程运行。
package Tread_pool;
import java.util.concurrent.*;
public class Test1 {
public static void main(String[] args) {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.DAYS.SECONDS,
new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
Runnable target = new MyRunnable();
//三个线程池的线程运行
pool.execute(target);
pool.execute(target);
pool.execute(target);
//任务队列,满了
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
//临时线程工作
pool.execute(target);
pool.execute(target);
//超了,之后报错
pool.execute(target);
pool.shutdown();
}
}
package Tread_pool;
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--> out 666~");
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行Callable任务(有返回结果)
package Tread_pool;
import java.util.concurrent.*;
public class Test2 {
public static void main(String[] args) throws Exception {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8, TimeUnit.DAYS.SECONDS,
new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
Future<String> f1 = pool.submit(new MyCallable(100));
Future<String> f2 = pool.submit(new MyCallable(200));
Future<String> f3 = pool.submit(new MyCallable(300));
Future<String> f4 = pool.submit(new MyCallable(400));
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
}
}
package Tread_pool;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
//描述线程任务和返回结果
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return Thread.currentThread().getName() + "线程求出了1-" + n + "的和是" + sum;
}
}
Executors
核心线程数量到底配置多少?
计算密集型的任务 : 核心线程数量 = CPU的核数 + 1
IO密集型的任务 : 核心线程数量 = CPU的核数 * 2
CPU的核数: 32线程
在大型的并行系统环境中使用Executors如果不注意可能会出现系统风险
并发与并行
并发的含义
进程中的线程是由CPU负责调度执行的,但CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行CPU会轮询为系统的每个线程服务,由于CPU切换的速度很快,给我们的感觉这些线程在同时执行,这就是并发。
并行的理解
在同一个时刻上,同时有多个线程在被CPU调度执行。
多线程到底是怎么执行的
并发与并行同时进行!
网络通信
InetAddress IP地址
import java.net.InetAddress;
public class Test1 {
public static void main(String[] args) throws Exception {
InetAddress ip1= InetAddress.getLocalHost();
System.out.println(ip1.getHostName());
System.out.println(ip1.getHostAddress());
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostName());
System.out.println(ip2.getHostAddress());
System.out.println(ip2.isReachable( 6000));
}
}
UDP通信![](https://i-blog.csdnimg.cn/blog_migrate/2eafe5837b6bbd9e5f0a2260a3e314a3.png)
客户端可以反复发送数据
客户端实现步骤
- 创建DatagramSocket对象(发送端对象)
- 使用while死循环不断的接收用户的数据输入,如果用户输入的exit则退出程序
- 如果用户输入的不是exit,把数据封装成DatagramPacket
- 使用DatagramSocket对象的send方法将数据包对象进行发送
- 释放资源
接收端可以反复接收数据
接收端实现步骤
- 创建DatagramSocket对象并指定口(接收端对象)
- 创建DatagramPacket对象接收数(数据包对象)
- 使用DatagramSocket对象的receie方法传入DatagramPacket对象
- 使用while死循环不断的进行第3步
package datagram_2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
//1、创建客户端对象
DatagramSocket socket = new DatagramSocket();
//2、创建数据包对象封装要发出去的数据
/* public DatagramPacket(byte buf[],int length,InetAddress address,int port)
参数一:封装要发出去的数据。
参数二:发送出去的数据大小(字节个数)
参数三:服务端的IP地址(找到服务端主机)
参数四:服务端程序的端口。
*/
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入想要传送的文案:");
String msg = sc.nextLine();
if ("exit".equals(msg)) {
System.out.println("欢迎下次光临!退出成功!");
socket.close();
break;
}
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 6666); //InetAddress.getByName("") 获得目标地址的IP地址
//3、开始正式发送这个数据包的数据
System.out.println("客服端发送成功!");
socket.send(packet);
}
socket.close(); //释放资源
}
}
package datagram_2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动---");
//1、创建一个服务端的对象
DatagramSocket socket = new DatagramSocket(6666);
//2、创建一个数据包对象,用于接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
//3、开始正式的使用数据包来接收客服端的数据
while (true) {
socket.receive(packet);
//4、从字节数组中,把接收的数据直接倒出来
int len = packet.getLength();
//接收多少就倒出多少
String rs = new String(buffer, 0, len);
System.out.println(rs);
// System.out.println(packet.getAddress().getHostAddress()); //获得客服端的IP地址
// System.out.println(packet.getPort()); //获得端口
System.out.println("----------------------");
//IDEA中实现UDP无连接通信时,自己做练习时,由于接收端和发送端在同一台电脑上,所以当接收端程序和发送端程序其中一个先运行时,就会占用指定端口,而后面运行的一端的端口号则变成随机(或默认)。
//即使用多线程运行两个代码,也会发生这样的情况。多线程的本质也是交替运行代码。
}
}
}
TCP通信
package TCP;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动成功---");
// 1、创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流。
InputStream is = socket.getInputStream();
// 4、把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
//5、使用数据输入流读取客户端发送过来的消息
String rs = dis.readUTF();
System.out.println(rs);
//其实我们也可以获取客户端的IP地址
System.out.println(socket.getRemoteSocketAddress());
dis.close();
socket.close();
}
}
package TCP;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
//1、创建一个socket对象,并同时申请与服务端的连接。
Socket socket = new Socket("10.251.29.41", 8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("der");
dos.close();
socket.close();
}
}
多发多收
注意当客户端通过exit将通信管道关闭后,服务端会报错,需要通过try-catch处理错误
package TCP2;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动成功---");
// 1、创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
// 3、从socket通信管道中得到一个字节输入流。
InputStream is = socket.getInputStream();
// 4、把原始的字节输入流包装成数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
//5、使用数据输入流读取客户端发送过来的消息
try {
String rs = dis.readUTF();
System.out.println(rs);
//其实我们也可以获取客户端的IP地址
System.out.println(socket.getRemoteSocketAddress());
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() +"离线了!");
socket.close();
dis.close();
break;
}
}
}
}
package TCP2;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
//1、创建一个socket对象,并同时申请与服务端的连接。
Socket socket = new Socket("10.251.29.41", 8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入传送的文案:");
String msg = sc.nextLine();
if(msg.equals("exit")){
System.out.println("退出成功!!");
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush(); //及时刷新
}
}
}
目前我们开发的服务端程序,:是否可以支持与多个客户端同时通信?
- 不可以的。
- 因为服务端现在只有一个主线程,只能处理一个客户端的消息。
解决方案:
package TCP3;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
//1、创建一个socket对象,并同时申请与服务端的连接。
Socket socket = new Socket("10.251.29.41", 8888);
//2、从socket通信管道中得到一个字节输出流,用来发数据给服务端程序
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入传送的文案:");
String msg = sc.nextLine();
if(msg.equals("exit")){
System.out.println("退出成功!!");
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush(); //及时刷新
}
}
}
package TCP3;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
System.out.println("---服务端启动成功---");
// 1、创建ServerSocket的对象,同时为服务端注册端口。
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 2、使用serverSocket对象,调用一个accept方法,等待客户端的连接请求
Socket socket = serverSocket.accept();
System.out.println("有人上线了:"+socket.getRemoteSocketAddress());
new ServerReaderThread(socket).start();
}
}
}
package TCP3;
import java.io.DataInputStream;
import java.io.InputStream;
import java.net.Socket;
public class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
String msg = dis.readUTF();
System.out.println(socket.getRemoteSocketAddress() + "send:" + msg);
} catch (Exception e) {
System.out.println("有人下线了:" + socket.getRemoteSocketAddress());
dis.close();
socket.close();
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BS架构,网站开发原理
要严格遵循BS架构的基本原理,才能输出
package TCP_BS;
import TCP3.ServerReaderThread;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server_bs {
public static void main(String[] args) throws IOException {
System.out.println("---服务器启动成功---");
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket socket = serverSocket.accept();
System.out.println("有人上线了:"+socket.getRemoteSocketAddress());
new ServerReadThread_bs(socket).start();
}
}
}
package TCP_BS;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReadThread_bs extends Thread{
private Socket socket;
public ServerReadThread_bs(Socket socket){
this.socket = socket;
}
@Override
public void run() {
OutputStream os = null;
try {
os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charset=UTF-8");
ps.println();
ps.println("<div style='color:black;font-size:120px;text-align:center'>der<div>");
ps.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用线程池优化,防止服务端崩溃
package TCO_BS_Demo;
import TCP_BS.ServerReadThread_bs;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server_bs {
public static void main(String[] args) throws IOException {
System.out.println("---服务器启动成功---");
ServerSocket serverSocket = new ServerSocket(8080);
ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
while (true) {
Socket socket = serverSocket.accept();
System.out.println("有人上线了:"+socket.getRemoteSocketAddress());
pool.execute(new ServerReadRunnable_bs(socket));
}
}
}
package TCO_BS_Demo;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
public class ServerReadRunnable_bs implements Runnable{
private Socket socket;
public ServerReadRunnable_bs(Socket socket){
this.socket = socket;
}
@Override
public void run() {
OutputStream os = null;
try {
os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
ps.println("HTTP/1.1 200 OK");
ps.println("Content-Type:text/html;charset=UTF-8");
ps.println();
ps.println("<div style='color:black;font-size:120px;text-align:center'>der<div>");
ps.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Java高级技巧
Junit单元测试框架
可以用来对方法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)
优点
- 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
- 不需要程序员去分析测试的结果,会自动生成测试报告出来。
项目需求
某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试
具体步骤
- 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)
- 为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
- 测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;
- 开始测试:选中测试方法,右键选择“JUnit运行”,如果测试通过则是绿色;如果测试失败,则是红色
反射
获取类
package reflect;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Student.class;
System.out.println(c1.getName());
System.out.println(c1.getSimpleName());
Class c2 = Class.forName("reflect.Student");
System.out.println(c1 == c2);
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c1 == c3);
}
}
package reflect;
public class Student {
}
反射获得构造器
package Constructors;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class Test2Constructors {
@Test
public void testGetConstructors() {
Class c = Cat.class;
Constructor[] constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "-->" + constructor.getParameterCount());
}
}
@Test
public void testGetConstructors1() throws Exception {
Class c = Cat.class;
Constructor constructor =
c.getDeclaredConstructor();
System.out.println(constructor.getName() + "-->" + constructor.getParameterCount());
constructor.setAccessible(true);
Object o = constructor.newInstance();
System.out.println(o);
Constructor constructor2 =
c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName() + "-->" + constructor2.getParameterCount());
constructor2.setAccessible(true);
Object o2 = constructor2.newInstance("der",3);
System.out.println(o2);
}
}
package Constructors;
public class Cat {
private String name;
private int age;
private Cat() {
System.out.println("无参构造器执行了!!");
}
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 Cat(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造器执行了!!");
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
反射获得成员变量
package Constructors;
import org.junit.Test;
import java.lang.reflect.Field;
public class Test3Field {
@Test
public void testGetField() throws NoSuchFieldException, IllegalAccessException {
//1、反射第一步:必须是先得到类的Class对象
Class c = Cat.class;
//2、获取类的全部成员变量。
Field[] fields = c.getDeclaredFields();
//3、遍历这个成员变量数组
for (Field field : fields) {
System.out.println(field.getName()+"-->"+field.getType());
}
//4、定位某个成员变量
Field fname = c.getDeclaredField("name");
System.out.println(fname.getName()+"-->"+fname.getType());
Field fage = c.getDeclaredField("age");
System.out.println(fage.getName()+"-->"+fage.getType());
// 赋值
Cat cat = new Cat();
fname.setAccessible(true);
fname .set(cat,"ketty");
System.out.println(cat);
//取值
String name = (String) fname.get(cat);
System.out.println(name);
}
}
package Constructors;
public class Cat {
private String name;
private int age;
public static final String COUNTRY = "China";
public static int a;
public Cat() {
System.out.println("无参构造器执行了!!");
}
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 Cat(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造器执行了!!");
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
private void run() {
System.out.println("跑的贼快~~");
}
public void eat() {
System.out.println("爱吃猫粮~");
}
private String eat(String name) {
return "最爱吃: " + name;
}
}
package Constructors;
import org.junit.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Tes4Method {
@Test
public void testGetMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1、反射第一步:先得到class对象。
Class c = Cat.class;
//2、获取类的全部成员方法。
Method[] methods = c.getDeclaredMethods();
//3、遍历这个数组中的每个方法对象
for (Method method : methods) {
System.out.println(method.getName()
+ "--->" + method.getParameterCount() +
"---->" + method.getReturnType());
}
// 4、获取某个方法对象
Method run=c.getDeclaredMethod( "run");// 拿run方法,无参数的
System.out.println(run.getName()+
"--->"+ run.getParameterCount()+
"---->" + run.getReturnType());
Method eat =c.getDeclaredMethod( "eat", String.class);
System.out.println(eat.getName()+
"--->"+ eat.getParameterCount()+
"---->" + eat.getReturnType());
Cat cat = new Cat();
run.setAccessible(true);
Object rs = run.invoke(cat);
System.out.println(rs);
}
}
package Constructors;
public class Cat {
private String name;
private int age;
public static final String COUNTRY = "China";
public static int a;
public Cat() {
System.out.println("无参构造器执行了!!");
}
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 Cat(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造器执行了!!");
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
private void run() {
System.out.println("跑的贼快~~");
}
public void eat() {
System.out.println("爱吃猫粮~");
}
private String eat(String name) {
return "最爱吃: " + name;
}
}