提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 常用快捷键
- IDEA IJ 的一些使用
- 注释
- 字面量
- 变量
- 强制类型转换
- 运算符
- 键盘录入技术
- 分支
- 循环结构
- 跳转关键字
- 随机数
- 数组
- 方法
- 算法----信号位思想flag
- 面向对象(oop)
- API
- String
- ArrayList
- static
- 面向对象----继承----extends
- 面向对象----语法
- 面向对象----多态
- 内部类----面向对象
- 常用API
- 日期与时间
- 包装类----toString、valueOf
- 正则表达式
- Arrays----数组排序
- 常见算法
- Lambda----简化 重写方法
- 集合
- 常见数据结构
- List
- 泛型
- Set
- 可变参数----"int...nums"
- 集合Collections工具类
- Map
- 嵌套集合
- 创建不可变集合----少用
- Stream流
- 异常
- 日志框架
- File
- 方法递归
- 字符级
- IO流
- 缓冲流
- 转换流
- 序列化对象
- 打印流
- Properties
- IO流框架
- 线程
- 网络编程
- 单元测试
- 反射
- 注解
- 动态代理
- XML
- XML解析技术
- XML检索技术:Xpath
- 设计模式
- 完结
常用快捷键
Ctrl + D 复制本行代码
Ctrl + X 剪切本行代码
Alt + Shift + 方向键 移动本行代码
Ctrl + Alt + T 在选定代码上,外加代码(分支或循环等等)
Ctrl + Alt + V 补全代码
Shift + F6 直接一次性改名称
Ctrl + Alt + L 格式化数组
IDEA IJ 的一些使用
File–Project Structure–可以修改Module JDK版本
注释
快捷键:Ctrl + / 或 Ctrl + Shift +/
字面量
System.out.println(‘\n’); 代表换行
System.out.println(‘\t’); 代表一个tab
String 接字符串
char 接字符
变量
格式:数据类型 变量名称 = 初始值;
数据类型 变量名称; 这是在定义一个变量。 int a;
变量名称 = 初始值; 这是在赋值。 a = 10;
强制类型转换
数据类型 变量1 = (数据类型)变量2;
int a = 20;
byte b = (byte) a;
运算符
取余:%
在Java中,两整数相除结果还是整数,想要有小数位要在变量后*1.0。
连接运算符 ‘+’
优先从左算起
自增自减运算符 ‘++’ ‘–’
运算符在变量前:先对变量进行运算,再使用变量。
运算符在变量后:先使用变量,再对变量进行运算。
赋值运算符
关系运算符
逻辑运算符
短路逻辑运算符
三元运算符
运算符优先等级
键盘录入技术
分支
if分支
switch分支
表达式类型只能是:byte、short、int、char
case给出的值不允许重复,且只能是字面量,不能是变量。
switch的穿透性
循环结构
知道循环几次用for,不知道循环几次用while。
for循环定义的变量只能在for循环内使用,while循环需要在循环外定义变量。
for循环
快捷写法:for+i 。
while循环
do-while循环
死循环
嵌套循环
跳转关键字
如果是嵌套循环,想要结束外部循环,要使用break OUT;
随机数
数组
静态初始化数组
动态初始化数组
数组的遍历
快捷键:a.for+i
数组中的元素交换
要定义临时变量
方法
方法定义技巧
1.分析方法是否需要申明返回值类型。
2.分析方法是否需要接收参数。
返回数组的方法的定义与调用
方法参数的传递机制:值传递
基本类型传递的是数据值,如果使用方法时方法改变变量值,但方法使用后,变量值不变。
引用类型传递的是地址值,如果使用方法时方法改变数组数值,方法使用后,数组数值将会改变。
方法重载
同一个类中,出现多个方法名称相同,但是形参列表是不同的,那么这些方法就是重载方法。
形参列表不同指的是形参的个数、类型、顺序不同。
return 关键字
算法----信号位思想flag
案例:使随机数不重复的方法
案例:找素数
面向对象(oop)
设计对象并使用
对象在内存中的运行机制
对象储存在堆内存中
变量名称 c1 储存的是对象在堆内存中的地址
成员变量的数据储存在堆内存中
构造器
this关键字
this出现在有参数构造器中的用法
封装----private
封装private
调用
标准JavaBean
JavaBean:也可以称为实体类,其对象可以用于在程序中封装数据。
快捷键
为成员变量提供getter 和setter方法时,快捷写法为直接鼠标右键选中Generate,然后选择Getter and Setter 。
写有参数构造器时,快捷写法为鼠标右键选中Generate,然后选择 Constructor 。
写无参数构造器时,快捷写法为鼠标右键选中Generate,然后选择 Constructor ,再选择 Select None 。
成员变量和局部变量的区别
API
String
概述
创建字符串对象的2种方式
方式二中常用第三、四种:(如下)
两种方式的区别
方式一原理
方式二原理
String类常用API
遍历
方法一:获取某个索引位置处的字符
方法二:将当前字符串转换成字符数组返回
截取----substring
方式一:根据开始和结束索引进行截取(包前不包后)。
方式二:从传入的索引处截取,截取到末尾。
替换----replace
分割----split
String字符串的内容比较----equals
字符串内容比较不能用“==”,因为它比较的是字符串的地址,无法比较内容。
ArrayList
概述
ArrayList添加元素的API
ArrayList的泛型使用
一般使用ArrayList都要使用泛型,这样才更规范。
ArrayList的常用API
遍历
使用 get 和 size 。
ArrayList边遍历边删除元素
static
static关键字----可以在任何地方被访问
修饰成员变量
修饰成员方法
注意事项
static-工具类
private 类名(){
//工具类私有方法,私有后无法使用该类new对象
}
static-代码块----面向对象
static-单例设计模式
单例第一步先将构造器私有化
private 类名 (){
}
饿汉单例
先提前创建一个对象
步骤:
懒汉单例
在真正需要对象的时候,才去创建一个对象(延迟加载对象)
private 私有静态成员变量,就无法直接调用成员变量。
面向对象----继承----extends
继承特点
子类不可以直接使用父类的私有方法
子类可以使用父类的静态成员变量
成员变量、成员方法访问特点
方法重写----@Override
构造器特点
public 子类 (){
super();
}
、
、
、
子类调用父类有参数构造器:
public 子类 (){
super(父类成员变量,父类成员变量,);
}
、
、
this、super 总结
面向对象----语法
包
权限修饰符----public等
final
常量----public static final
枚举–enum
抽象类–abstract
特征和注意事项
模块方法模式
接口–interface
接口会默认公开,所以常量可以省略public static final 不写,抽象方法可以省略public abstract不写。
接口多实现–implement
接口多继承
接口新增方法
注意事项
面向对象----多态
优势
多态下引用数据类型的类型转换
//父类
public class User {
}
//子类
public class Customer extends User{
}
//子类
public class Business extends User {
}
//创建几个对象
User user;
Customer customer;
Business business;
子类对象转为父类类型----自动类型转换
User user = business;
User user = customer;
父类对象转为子类类型----强制类型转换
Customer customer = (Customer)user;
Business business = (Business)user;
instanceof----判断当前对象的真实类型
//判断当前父类对象是哪个子类类型
user instanceof Customer
内部类----面向对象
静态内部类
成员内部类
局部内部类
匿名内部类
使用形式
常用API
Object
toString----返回对象内容,而不是返回地址
快捷键:子类重写直接用Genenrate–toString() 。
@Override
public String toString() {
return "Movie{" +
"name='" + name + '\'' +
", score=" + score +
", actor='" + actor + '\'' +
'}';
}
equals----比较内容是否相同,而不是地址
子类重写直接用Generate–equals() and hashCode() , 然后去掉hashCode() 。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Movie movie = (Movie) o;
return Double.compare(movie.score, score) == 0 && Objects.equals(name, movie.name)
&& Objects.equals(actor, movie.actor);
}
Objects----new对象进行内容比较时使用
Objects.equals----避免空指针异常
Objects.equals(s1, s2)
StringBuilder----拼接数组里的字符
StringBuilder sb = new StringBuilder();
可以使用链式结构
变量.append().append().append().reverse();
//拼接数组里的字符
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
sb.append(i == arr.length-1 ? arr[i] : arr[i] + ", ");
}
sb.append("]");
System.out.println(sb);
拿出StringBuilder里的字符
//遍历拿出所以字符
for (int i = 0; i < sb.length(); i++) {
char ch = sb.charAt(i);
}
Math
System
BigDecimal----解决浮点型运算失真的问题
//获取BigDecima对象
double a = 12;
double b = 5;
BigDecimal a = BigDecimal.valueOf(a);//将数据a封装到BigDecimal中。
BigDecimal b = BigDecimal.valueOf(b);
double c = a.divide(b);
double c = BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b)).doubleValue();
日期与时间
Data----getTime
SimpleDataFormat----format 、 parse
format----将Data对象格式成时间格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a ");
//EEE 代表 星期几 , a 代表 上下午 。
parse----将字符串时间格式解析成Data对象
字符串日期格式必须与SimpleDataFormat的格式相同。
Calendar
Calendar cal = Calendar.getInstance();
//直接调方法,不是new对象。
JDK8开始新增日期API----日期
LocalDate、LocalTime、LocalDateTime
Instant
DateTimeFormatter
Duration/Period
ChronoUnit
包装类----toString、valueOf
toString----把基本类型数据转成字符串类型数据
//把数组内容转成集合,可以直接输出
Arrays.toString();
valueOf----把字符串类型数据转成基本类型数据
//直接调用valueOf,不用记的麻烦。
String s = Integer.valueOf();
String s = Double.valueOf();
正则表达式
匹配规则----matches
matches("\\d") \\要求字符串内容为一个,且范围为0-9。
matches("\\d{4}") \\要求字符串内容要大于4个,且范围为0-9。
matches("\\d{4,20}") \\要求字符串内容要大于4个,且小于20个,且范围为0-9。
matches("1[3-9]\\d{9}") \\要求字符串第一个字符为1,第二个字符范围为3-9,其余的字符个数为9且范围为0-9。
matches("\\.") \\要求字符为"." 。
matches("1?") \\要求1出现一次或者不出现。 //以此类推*、+ 。
爬取信息
Arrays----数组排序
使用binarySearch时,必须让数组排好序,否则会出bug 。
Arrays.toString() //打印数组内容
自定义类的排序----Comparator
自定义类排序:
Arrays.sort(Student, new Comparator<Student>() {
//如果是自定义的学生类,想要按照年龄、身高来排序,方法如下:
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
return Double.compare(o1.getHeight() , o2.getHeight());
//小数的比较。因为return返回的是int类型。
}
});
常见算法
选择排序
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i+1 ; j < arr.length; j++) {
if (arr[i] > arr[j]){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
二分查找
public static int search(int [] arr , int data){
int left = 0;
int right = arr.length-1;
while (left <= right){
int middle = (left + right)/2;
if (data > arr[middle]){
left = middle +1;
}else if (data < arr[middle]){
right = middle -1;
}else {
return middle;
}
}
return -1;
}
Lambda----简化 重写方法
Swimming s = new Swimming() {
@Override
public void swim() {
System.out.println("");
}
};
Swimming s1 = () ->{
System.out.println("");
};
go(new Swimming() {
@Override
public void swim() {
System.out.println("");
}
});
go(() ->{
System.out.println("");
});
集合
Collection集合体系
Collection常用API
Collection的遍历方式
迭代器----Iterator
//遍历方式
Iterator<Integer> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
foreach/增强for循环----也可遍历数组
//快捷键
数组或集合名.for 回车
lambda表达式----forEach
//代码简化
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.forEach( s ->{
System.out.println(s);
});
list.forEach( s -> System.out.println(s));
list.forEach(System.out::println);
Collection存储自定义类型的对象
//定义一个电影类
Collection<Movie> movies = new ArrayList<>();
movies.add(new Movie("肖申克",9.9,"小李子"));
movies.add(new Movie("成立",9.3,"陈列"));
movies.add(new Movie("肖克",9.6,"李子"));
//遍历----foreach
for (Movie movie : movies) {
System.out.println(movie.getName());
System.out.println(movie.getScore());
System.out.println(movie.getActor());
}
Collection集合体系小结
常见数据结构
栈
队列
数组
链表
二叉树
特点:
二叉查找树
平衡二叉树
红黑树
List
List<String> list = new ArrayList<>();
List特有API
List<String> list = new ArrayList();
list.indexOf(输入字符);//根据输入字符,提取字符所在的索引
list.substring(输入索引);//根据输入的索引,提取相对应的字符串
List的遍历方式
1.迭代器
2.foreach
3.lambda
4.for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
ArraysList集合底层原理
LinkedList集合底层原理
集合的并发修改异常问题----边遍历边删除
Iterator<String> it = list.iterator();
while (it.hasNext()){
String ele = it.next();
if (ele.equals("要删除的对象")){
it.remove(); //迭代器自己的方法
}
}
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("1")){
list.remove(i);
i--;
}
}
for (int i = list.size()-1; i >=0 ; i--) {
String s = list.get(i);
if (s.equals("1")){
list.remove(i);
}
}
泛型
概述
自定义泛型类
自定义泛型方法
自定义泛型接口
泛型通配符、上下限
Set
Set<String> set = new HashSet<>();
HashSet元素无序的底层原理:哈希表
HashSet元素去重复的底层原理
LinkedHashSet
TreeSet
自定义排序规则----Comparable
方式一:类自定义比较规则。
//接口Comparable
public class Student implements Comparable<Student> {
private String name;
private int age;
private double height;
//重写排序规则
@Override
public int compareTo(Student o) {
return this.age - o.age;//相同的元素会去掉
return this.age - o.age >= 0 ? 1 :-1 ; //相同的元素可以保留。
}
}
方式二:集合自带比较器对象进行规则自定。
Set<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getAge() - o2.getAge();
return Double.compare(o1.getHeight() , o2.getHeight());//浮点型比较
}
});
简化:
Set<Student> set = new TreeSet<>(( o1, o2) ->
Double.compare(o1.getHeight() , o2.getHeight()) );
Set<Student> set = new TreeSet<>(( o1, o2) -> o1.getAge() - o2.getAge() );
可变参数----“int…nums”
public static void sum(int...nums){
}
集合Collections工具类
API----addAll、shuffle
Collections.addAll(list,"");
Collections.shuffle(list);
List集合自定义排序规则----Collections.sort
方式一:
Collections.sort(list);//在自定义类中要先重写比较规则,然后可以直接调用。
//使用接口Comparable
public class Student implements Comparable<Student> {
//重写排序规则
@Override
public int compareTo(Student o) {
return this.age - o.age;
}
}
方式二:
List<> list =new Arrayslist<>();
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(),o2.getHeight());//浮点型
return o1.getAge() - o2.getAge();
}
});
简化:
Collections.sort(list,( o1, o2) -> Double.compare(o1.getAge(),o2.getAge()) );
Map
概述
体系特点
Map<String,Integer> map = new HashMap<>();
常用API
遍历方式
键找值----keySet、get
Map<String,Integer> map = new HashMap<>();
//先拿到全部键
Set<String> keys = map.keySet();
//再遍历得每个键的值
for (String key : keys) {
int value = map.get(key);//获取值
System.out.println(key +"===>" + value);
}
键值对----entrySet、getKey、getValue
Map<String,Integer> map = new HashMap<>();
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
String key =entry.getKey();
int value = entry.getValue();
System.out.println(keys +"===>" + value);
}
lambda表达式----forEach
map.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println(s + "---->" + integer);
}
});
简化:
map.forEach(( s, integer) -> {
System.out.println(s + "---->" + integer);
});
案例
//随机输出定义的4个数据0
String[] s = {"A","B","C","D"};
StringBuilder stringBuilder = new StringBuilder();
Random r = new Random();
for (int i = 0; i < 80; i++) {
stringBuilder.append(s[r.nextInt(4)]);
}
System.out.println(stringBuilder);
Map<Character,Integer> map = new HashMap<>();
//
for (int i = 0; i < stringBuilder.length(); i++) {
char ch = stringBuilder.charAt(i);
if (map.containsKey(ch)) {
map.put(ch,map.get(ch) + 1);
}else {
map.put(ch,1);
}
}
System.out.println(map);
Map集合的实现类----与Set集合差不多
HashMap
LinkedHashMap
TreeMap----自定义排序规则
方式一:类自定义比较规则。
//接口Comparable
public class Student implements Comparable<Student> {
private String name;
private int age;
private double height;
//重写排序规则
@Override
public int compareTo(Student o) {
return this.age - o.age;//相同的元素会覆盖
}
}
//main方法中直接输出
Map<Student , Integer> map = new TreeMap<>();
System.out.println(map);
方式二:集合自带比较器
Map<Student , Integer> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getHeight(),o2.getHeight());//浮点型
}
});
嵌套集合
//嵌套集合格式:
Map<String, List<String>> map = new HashMap<>();
List<String> list = new ArrayList<>();
Collections.addAll(list,"A","B");
map.put("小米",list);
System.out.println(map);
Map<String, List<String>> map = new HashMap<>();
//创建集合,将list集合中的元素添加到Map集合中
List<String> list1 = new ArrayList<>();
Collections.addAll(list1,"A","C");//直接添加多个数据
map.put("小米",list1);
List<String> list2 = new ArrayList<>();
Collections.addAll(list2,"A","D");
map.put("小蓝",list2);
List<String> list3 = new ArrayList<>();
Collections.addAll(list3,"A","B","C");
map.put("小新",list3);
System.out.println(map);
//统计数据,算出map中值重复了多少
Map<String,Integer> infos = new HashMap<>();//创建集合储存map中的值
Collection<List<String>> values = map.values();//拿到map中的所有值,将它储存在Collection集合中。
System.out.println(values); //values = [[A, D], [A, B, C], [A, C]]
//遍历集合,把集合中的元素遍历出来
for (List<String> value : values) {//这个是遍历Collection集合中所有的list集合
for (String s : value) {//这是遍历list集合里的每个元素
//判断infos集合中是否有这个元素
if (infos.containsKey(s)){
infos.put(s,infos.get(s) + 1);
}else {
infos.put(s,1);
}
}
}
System.out.println(infos);
创建不可变集合----少用
JDK8不可用
List<Double> list = List.of(12.1,34.5,67.8);
Stream流
概述
//名字分类,传统办法:
List<String> name = new ArrayList<>();
Collections.addAll(name,"张倩", "张强前","张作者","周中");//添加多个元素
//按姓氏分
List<String> zhangName = new ArrayList<>();
for (String s : name) {
if (s.startsWith("张")){
zhangName.add(s);
}
}
System.out.println(zhangName);
//按姓名长度分
List<String> zhangThereName = new ArrayList<>();
for (String s : zhangName) {
if (s.length()==3){
zhangThereName.add(s);
}
}
System.out.println(zhangThereName);
//stream流一步完成//可以支持链式结构
name.stream().filter(s-> s.startsWith("张")).
filter(s-> s.length()==3).forEach(s -> System.out.println(s));
集合、数组获取Stream流
//Collection集合获取流
List<String> list = new ArrayList<>();
Stream<String> s = list.stream();
//Map集合获取流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();//键获取流
Stream<Integer> valueStream = map.values().stream();//值获取流
Stream<Map.Entry<String,Integer>> keyAndKeyStream = map.entrySet().stream();//键值对获取流
//数组获取流
String[] names = {"小米","小红","小新"};
Stream<String> nameStream = Arrays.stream(names);
Stream<String> nameStream1 = Stream.of(names);
Stream常用API
name.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
});
简化:
name.stream().filter(s-> s.startsWith("张")).
//遍历
).forEach(s -> System.out.println(s));
简化:
).forEach(System.out::println);
//返回流长度
long size = list.stream().count();
//map加工方法
//给集合元素前加上字符串
list.stream().map(s -> "" + s).
//把集合所有的元素都加工成Student对象
list.stream().map(s -> new Student(s)).forEach(s -> System.out.println(s));
简化:
list.stream().map(Student::new).forEach(System.out::println);
//合并两个流
Stream<String> = Stream.concat(stream1,stream2);
综合应用
one.stream().max((e1,e2) -> Double.compare(e1.getSalary()+e1.getBonus() ,e2.getSalary()+e2.getBonus())).
map(e -> new Top(e.getName(),e.getSalary()+e.getBonus())).get();
//max方法自定义类比较方法,直接取出最大值对象,map方法将对象储存到新的类对象中。
one.stream().sorted((e1,e2) -> Double.compare(e1.getSalary()+e1.getBonus() ,e2.getSalary()+e2.getBonus()))
.skip(1).limit(one.size()-2).forEach(s -> {
allMoney += s.getBonus() + s.getSalary();//要在main方法外定义一个公开静态成员变量
});
//sorted方法自定义类比较规则, 将集合中的对象排序,
收集Stream流
//List集合
Stream<String> s1 = name.stream().filter(s -> s.startsWith("张"));
List<String> list = s1.collect(Collectors.toList());
System.out.println(list);
//stream流只能使用一次,再次使用时要重写stream() 。
//Set集合
Stream<String> s2 = name.stream().filter(s -> s.startsWith("张"));
Set<String> set = s2.collect(Collectors.toSet());
System.out.println(set);
//数组
Stream<String> s3 = name.stream().filter(s -> s.startsWith("张"));
Object[] arr =s3.toArray();
System.out.println(Arrays.toString(arr));
异常
概述
常见运行时异常
常见编译时异常
异常的默认处理流程
编译时异常的处理机制
try…catch…——出现异常可以使程序正常运行
快捷键:Ctrl + Alt + T ----try/catch
运行时异常的处理机制
异常处理使代码更稳健
//使异常出现后代码可以继续运行,而不会程序死亡。
Scanner sc = new Scanner(System.in);
while (true){
try {//可能出现异常的代码行
System.out.println("请输入定价:");
String priceStr = sc.nextLine(); //只能输入一行代码。
double price = Double.valueOf(priceStr);//转换成double类型
if (price > 0){
System.out.println("定价:"+price);
break;
}else {
System.out.println("输入有误");
}
} catch (Exception e) {//处理异常,使出现异常后代码可以继续运行
System.out.println("重写输入");
}
}
自定义异常
编译时异常
//定义一个异常类继承Exception
public class Text extends Exception{
//重写构造器
public Text(String message) {
super(message);
}
public Text() {
}
}
public static void main(String[] args) {
try {
checkAge(-34);
} catch (Text e) {
throw new RuntimeException(e);
}
}
public static void checkAge(int age) throws Text {
if (age < 0 || age >200){
//抛出一个异常对象给调用者
//throw:在方法内部直接创建一个异常对象,并从此点抛出
//throws:用在方法申明上的,抛出方法内部的异常。
throw new Text(age + " is false");
}else {
System.out.println("合法");
}
}
运行时异常
//定义一个类继承RuntimeException
public class Text extends RuntimeException{
public Text(String message) {
super(message);
}
public Text() {
}
}
public static void main(String[] args) {
try {
checkAge(-34);
} catch (Text e) {
throw new RuntimeException(e);
}
}
public static void checkAge(int age) throws Text {
if (age < 0 || age >200){
//抛出一个异常对象给调用者
//throw:在方法内部直接创建一个异常对象,并从此点抛出
//throws:用在方法申明上的,抛出方法内部的异常。
throw new Text(age + " is false");
}else {
System.out.println("合法");
}
}
日志框架
概述
技术体系
Logback
概述
快速入门
public static final Logger LOGGER = LoggerFactory.getLogger("MovieSystem.class");
//logback.xml 文件代码
//<file>和<fileNamePattern>文件要自己改变路径
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>D:\Logback\data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>D:\Logback\data-%d{yyyy-MM-dd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR | ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="ALL">
<!-- 注意:如果这里不配置关联打印位置,该位置将不会记录日志-->
<appender-ref ref = "CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
//记录系统的日志信息,会在路径生成TXT文档
public static final Logger LOGGER = LoggerFactory.getLogger("Text");
public static void main(String[] args) {
try {
LOGGER.debug("开始运行");
LOGGER.info("开始记录日志");
int a = 10;
int b = 0;
LOGGER.trace("a:" + a);
LOGGER.trace("b:" + b);
System.out.println(a/b);
} catch (Exception e) {
e.printStackTrace();
LOGGER.error("功能出现异常" + e);
}
}
配置详情
输出位置、格式设置
<appender-ref ref = "CONSOLE"/> //会打印在控制台中,不打就不会影响运行流程。
<appender-ref ref="FILE" /> //会打印在生产文件中
<!--输出流对象 默认 System.out 改为 System.err-->//改完后打印内容会变红
<target>System.out</target>
//符号代表的意思
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
日志级别设置
<root level="ALL">
File
概述
//绝对路径
//路径的三种写法
File file = new File("D:\\Logback\\data.log");
File file = new File("D:/Logback/data.log");
File file = new File("D:" +File.separator+ "Logback"+ File.separator+"data.log");
//相对路径
//创建一个New Project,而不是Empty Module。
//从src目录开始查找就行
File f = new File("src/data.txt");
常用API
判断文件类型、获取文件信息
创建、删除文件
遍历
方法递归
概述
算法流程
public static void main(String[] args) {
System.out.println(f(5));
//f(4)*5
//f(3)*4*5
//f(2)*3*4*5
//f(1)*2*3*4*5
}
//阶乘计算
public static int f(int n){
if (n == 1){
return 1;
}else {
return f(n-1)*n;
}
}
public static void main(String[] args) {
System.out.println(f(100));
}
//求和计算
public static int f(int n){
if (n == 1){
return 1;
}else {
return f(n-1)+n;
}
}
案例
//公式:f(x) - f(x)/2 - 1 = f(x-1)
// f(x) = 2*f(x) + 2
public static void main(String[] args) {
System.out.println(f(1));
}
public static int f (int n){
if (n == 10){
return 1;
}else {
return 2*f(n+1) + 2;
}
}
非规律化递归
文件搜索
public static void main(String[] args) {
search(new File("D:/"),"#ib_redo6");
}
//文件搜索方法
public static void search (File dir , String fileName){
//判断源dir是否是目录
if (dir != null && dir.isDirectory()){
//提取一级文件
File [] files = dir.listFiles();
//判断一级文件对象集合是否存在
if (files != null && files.length > 0 ){
//遍历所有一级文件对象
for (File file : files) {
//判断一级文件对象是文件还是目录
if (file.isFile()){
//为文件的话,判断是否为要查找的文件
if (file.getName().contains(fileName)){
System.out.println("找到了" + file.getAbsolutePath());
}
}else {
//为目录,流程重复
search(file , fileName);
}
}
}
}else {
System.out.println("目录不存在");
}
}
啤酒问题
public static void main(String[] args) {
buy(10);
}
//定义一个变量来储存总共买酒的数量
public static int totalNumber;
//定一个义变量储存每次用酒瓶换完酒后酒瓶剩余的数量
public static int lastBottleNumber;
//定一个义变量储存每次用瓶盖换完酒后瓶盖剩余的数量
public static int lastCoverNumber;
//买酒方法
public static void buy(int money){
//直接计算钱可以买多少酒
int buyNumber = money / 2;
totalNumber += buyNumber;
//统计本轮瓶子数和盖子数,要加上上次换酒后剩的。
int BottleNumber =lastBottleNumber + buyNumber;
int CoverNumber = lastCoverNumber + buyNumber;
//拿酒瓶换钱
int allMoney = 0;
if (BottleNumber >= 2){
allMoney += (BottleNumber/2) * 2;
}
lastBottleNumber = BottleNumber%2;
//拿瓶盖换钱
if (CoverNumber >= 4){
allMoney += (CoverNumber/4) * 2;
}
lastCoverNumber = CoverNumber%4;
//判断是否可以继续买酒
if (allMoney >= 2){
buy(allMoney);
}else {
System.out.println("酒的总数" + totalNumber);
System.out.println("剩余瓶子数" + lastBottleNumber);
System.out.println("剩余盖子数" + lastCoverNumber);
}
}
字符级
概述
解码、编码操作
String name = "abc我爱你";
byte [] bytes = name.getBytes();//将字符串默认编码成字节
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));//输出字节编码
String rs = new String(bytes);//默认解码
System.out.println(rs);
String name = "abc我爱你";
byte [] bytes = name.getBytes("GBK");//指定编码
System.out.println(bytes.length);
System.out.println(Arrays.toString(bytes));
String rs = new String(bytes,"GBK");//指定解码
System.out.println(rs);
IO流
概述
字节流的使用
文件字节输入流:每次读取一个字节
//创建字节输入流管道与源文件路径接通
InputStream is = new FileInputStream(new File("src/data"));
InputStream is = new FileInputStream("src/data");//简化写法
//读取一个字符
int b1 = is.read();
System.out.println((char) b1);
int b2 = is.read();
System.out.println((char) b2);
int b3 = is.read();
System.out.println((char) b3);
//循环读取所有字符
//但是不能读取中文,因为read每次只能读取一个字节,而中文包含三个字节,会出现bug
int b;
while ((b = is.read()) != -1){
System.out.print((char) b);
}
文件字节输入流:每次读取一个字节数组
InputStream is = new FileInputStream("src/data");
//定义一个字节数组,用来储存读取的字节
byte[] buffer = new byte[3];
int len = is.read(buffer);//读取字节到数组中,返回字节个数
System.out.println(len);//输出读取字节个数
//编译字节数组
//但是当字节数组个数不满足3个时,会出现bug
String rs = new String(buffer);//解码
System.out.println(rs);
//读取多少字节就从数组中输出多少个字节,不会出bug
String rs = new String(buffer,0,len);//解码,把字节转成字符串
System.out.println(rs);
InputStream is = new FileInputStream("src/data");
//循环读取所有字符
byte [] buffer = new byte[3];
int len;//记录每次读取的字节数
while ((len = is.read(buffer)) != -1){
System.out.print(new String(buffer, 0, len));//解码
}
//但是此种方式还是会出现bug,当字符形式为“ab我”时,为了读取3个字符,会把中文的三个字符拆出来读取一个。
文件字节输入流:一次读完全部字节
//创建一个文件储存所有字节
File f = new File("src/data");
InputStream is = new FileInputStream(f);
//定义一个字节数组,大小和文件一样
byte [] buffer = new byte[(int) f.length()];
int len = is.read(buffer);//读取字节到数组中//编码
System.out.println(len);
System.out.println(f.length());
System.out.println(new String(buffer));//解码
//读取全部字节的API
//但是这个API是从JDK9才开始支持
byte [] buffer = is.readAllBytes();
System.out.println(new String(buffer));
文件字节输出流:写字节数据到文件
//创建一个文件字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("src/date.txt");//每次输出前,会先清空文件之前的数据
OutputStream os = new FileOutputStream("src/date.txt",true);//每次输出可以叠加之前的数据
//写一个字节出去
os.write('a');
os.write(97);
os.write('徐');//无法输出,因为每次只能输出一个字节
os.write("\r\n".getBytes());//换行
//写一个数组出去
byte [] buffer = {98,'a','b'};
os.write(buffer);
os.write("\r\n".getBytes());//换行
//将一个字符串转成字节储存到数组
byte [] buffers = "abc我是中国人".getBytes();
os.write(buffers);//输出数组
os.write("\r\n".getBytes());
//输出数组的一部分
byte [] buffer1 = {'a',97,98};
os.write(buffer1,0,1);//就会输出“a、97”
//写完数据,一定要刷新或释放数据
//写完数据,必须刷新数据,把缓存的数据到输出。//使用后可以继续使用流
os.flush();
//释放资源,包含刷新数据。//使用后不可以继续使用流
os.close();
文件拷贝
//拷贝视频、文件、图片
try {
//输入流
InputStream is = new FileInputStream("");
//输出流
OutputStream os = new FileOutputStream("");
//数组储存字节
byte[] buffer = new byte[1024];
int len;//每次读取长度
//循环输出字节数组
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("完成");
//关闭流
os.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
}
资源释放的方式
try-catch-finally
//定义在try外,finall才能执行
InputStream is = null;
OutputStream os = null;
//拷贝视频、文件、图片
try {
//输入流
is = new FileInputStream("");
//输出流
os = new FileOutputStream("");
//数组储存字节
byte[] buffer = new byte[1024];
int len;//每次读取长度
//循环输出字节数组
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("完成");
} catch (Exception e) {
e.printStackTrace();
//无论代码是正常还是出现异常都会执行这里
} finally {
//关闭流
try {
//非空判断
if (os != null)os.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
//非空判断
if (os != null)is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
try-with-resource
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
InputStream is = new FileInputStream("");
//输出流
OutputStream os = new FileOutputStream("");
){
//数组储存字节
byte[] buffer = new byte[1024];
int len;//每次读取长度
//循环输出字节数组
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("完成");
} catch (Exception e) {
e.printStackTrace();
}
字符流的使用
文件字符输入流:一次读取一个字符
//创建一个字符输入流管道与源文件相接
Reader fr = new FileReader("src/data.txt");
//读取一个字符
int code = fr.read();
System.out.println((char) code);
//循环读取所有字符
int code;
while ((code = fr.read()) != -1){
System.out.print((char) code);
}
文件字符输入流:一次读取一个字符数组
//创建一个字符输入流管道与源文件相接
Reader fr = new FileReader("src/data.txt");
//用循环读取一个字符数组数据
char [] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1){
String rs = new String(buffer,0,len);
System.out.println(rs);
}
文件字符输出流
//创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("src/data");//覆盖管道,会清空之前文件的数据
Writer fw = new FileWriter("src/data",true);//追加数据,不会清空之前文件的数据
//写一个字符出去
fw.write('a');
fw.write(97);
fw.write('徐');//不会出bug
fw.write("\r\n");//换行
//写一个字符串出去
fw.write("abc我是中国人");
fw.write("\r\n");//换行
//写一个字符数组出去
char [] chars = "abc我是中国人".toCharArray();//把字符串转成字符
fw.write(chars);
fw.write("\r\n");//换行
//写字符串的一部分出去
fw.write("abc我是中国人",0,3);
fw.write("\r\n");//换行
//写字符数组的一部分出去
fw.write(chars,3,5);
fw.write("\r\n");//换行
fw.flush();//刷新流
fw.close();//关闭流
缓冲流
概述
字节缓冲流
//拷贝视频、文件、图片
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
InputStream is = new FileInputStream("");
//把原始的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is);
//输出流
OutputStream os = new FileOutputStream("");
//把原始的字节输出流包装成高级的缓冲字节输出流
OutputStream bos = new BufferedOutputStream(os);
){
//数组储存字节
byte[] buffer = new byte[1024];
int len;//每次读取长度
//循环输出字节数组
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("完成");
} catch (Exception e) {
e.printStackTrace();
}
字节缓冲流的性能分析
原始流加上8kb的桶,效果和高级流一样
//高级流拷贝
//拷贝视频、文件、图片
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
InputStream is = new FileInputStream("");
//把原始的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is);
//输出流
OutputStream os = new FileOutputStream("");
//把原始的字节输出流包装成高级的缓冲字节输出流
OutputStream bos = new BufferedOutputStream(os);
){
//数组储存字节
byte[] buffer = new byte[1024];
int len;//每次读取长度
//循环输出字节数组
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("完成");
} catch (Exception e) {
e.printStackTrace();
}
//原始流加上8kb的桶,效果和高级流一样
//原始流拷贝
//拷贝视频、文件、图片
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
InputStream is = new FileInputStream("");
//输出流
OutputStream os = new FileOutputStream("");
){
//数组储存字节
byte[] buffer = new byte[1024 * 8];
int len;//每次读取长度
//循环输出字节数组
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println("完成");
} catch (Exception e) {
e.printStackTrace();
}
字符缓冲流
输入流
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
Reader fr = new FileReader("src/data");
//把原始的字符输入流包装成高级的缓冲字符输入流
BufferedReader br = new BufferedReader(fr);
){
//数组储存字符
char[] buffer = new char[1024];
int len;//每次读取长度
//循环输出字符数组
while ((len =br.read(buffer)) != -1) {
String rs = new String(buffer,0,len);
System.out.print(rs);
}
} catch (Exception e) {
e.printStackTrace();
}
//经典代码
try (
//这里面只能放置资源对象,用完会自动关闭
//自动调用资源对象close方法关闭资源(即使出现异常也会关闭操作)
//输入流
Reader fr = new FileReader("src/data");
//把原始的字符输入流包装成高级的缓冲字符输入流
BufferedReader br = new BufferedReader(fr);
){
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
输出流
//创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("src/data");//覆盖管道,会清空之前文件的数据
Writer fw = new FileWriter("src/data01",true);//追加数据,不会清空之前文件的数据
//把低级字符输出流包装成缓冲字符输出流
BufferedWriter bw = new BufferedWriter(fw);
//写一个字符出去
bw.write('a');
bw.write(97);
bw.write('徐');//不会出bug
bw.newLine();//bw.write("\r\n");//换行
//写一个字符串出去
bw.write("abc我是中国人");
bw.write("\r\n");//换行
//写一个字符数组出去
char [] chars = "abc我是中国人".toCharArray();
bw.write(chars);
bw.newLine();//bw.write("\r\n");//换行
//写字符串的一部分出去
bw.write("abc我是中国人",0,3);
bw.newLine();//bw.write("\r\n");//换行
//写字符数组的一部分出去
bw.write(chars,3,5);
bw.newLine();//bw.write("\r\n");//换行
fw.flush();//刷新流
bw.close();//关闭流
案例
try (
//创建字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("src/data.txt"));
//创建一个字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("src/date"));
){
//创建一个集合储存读取文件所有数据
List<String> data = new ArrayList<>();
//循环读取数据
String line;
while ((line = br.readLine()) != null){
data.add(line);
}
System.out.println(data);
//自定义排序规则
List<String> sizes = new ArrayList<>();
Collections.addAll(sizes,"一","二","三","四","五","六","七");
Collections.sort(data, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
//indexOf是根据字符所在的位置返回集合的索引//根据字符提取索引
//substring是根据开始索引和最终索引提取这一段的字符//根据索引提前字符
return sizes.indexOf(o1.substring(0,o1.indexOf("、"))) -
sizes.indexOf(o2.substring(0,o2.indexOf("、")));
}
});
System.out.println(data);
//循环遍历集合
for (String datum : data) {
bw.write(datum);
bw.newLine();//换行
}
} catch (Exception e) {
e.printStackTrace();
}
转换流
字符输入转换流
//字节输入流
InputStream is = new FileInputStream("src/");
//字节输入流转换成字符输入流
Reader isr = new InputStreamReader(is);//默认以UTF-8的方式转成字符
Reader isr = new InputStreamReader(is, "GBK");//以指定GBK编码方式转成字符
//把字符输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
字符输出转换流
//字节输出流
OutputStream os = new FileOutputStream("src");
//把字节输出流转成字符输出流
Writer bos = new OutputStreamWriter(os);//以默认UTF-8发生输出字符
Writer bos = new OutputStreamWriter(os,"GBK");//以指定GBK方式输出字符
//把字符输出流包装成缓冲字符输出流
BufferedWriter bw = new BufferedWriter(bos);
bw.write();
bw.close();
序列化对象
对象序列化
//创建学生对象
Student s = new Student("阿康","ak","123",20);
//对象序列化:使用对象字节输出流包装字节输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("src/dd"));
//调用序列化方法
oos.writeObject(s);
//释放资源
oos.close();
//对象要实现序列化,必须实现Serializable接口
public class Student implements Serializable {
}
对象反序列化
//创建对象字节输入流管道包装字节输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/dd"));
//调用对象字节反序列化方法
Student s = (Student) ois.readObject();
System.out.println(s);
对象序列化规则:
//对象要实现序列化,必须实现Serializable接口
//申明序列化的版本号
//序列化的版本号和反序列化的版本号必须一致才不会出错
//transient修饰的成员变量不参与序列化
//对象要实现序列化,必须实现Serializable接口
public class Student implements Serializable {
//申明序列化的版本号
//序列化的版本号和反序列化的版本号必须一致才不会出错
private static final long serialVersionUID = 1;
private String name;
private String loginName;
//transient修饰的成员变量不参与序列化
private transient String passWord;
private int age;
public Student() {
}
}
打印流
PrintStream、PrintWriter
//两种写法
PrintStream ps = new PrintStream("src/ps.txt");
PrintStream ps = new PrintStream(new FileOutputStream("src/ps.txt"));
//要追加数据只能使用低级管道
PrintStream ps = new PrintStream(new FileOutputStream("src/ps.txt",true));
输出语句的重定向
//改变输出语句的位置
PrintStream ps = new PrintStream("src/ps");
System.setOut(ps);//把系统打印流改成我们自己的打印流
System.out.println("锦瑟无端五十闲");
System.out.println("一线一柱思华年");
Properties
//使用Properties把键值对信息存到文件中去
Properties pp = new Properties();
//信息输入
pp.setProperty("heima","123");
System.out.println(pp);
pp.store(new FileWriter("src/pp"),"i am happy");
//Properties读取属性文件中的键值对信息
Properties properties = new Properties();
System.out.println(properties);
//加载属性文件中键值对数据到属性对象Properties中去
properties.load(new FileReader("src/pp"));
System.out.println(properties);
//通过键拿到对应的值
String rs = properties.getProperty("heima");
System.out.println(rs);
IO流框架
//文件复制
IOUtils.copy(new FileInputStream("") , new FileOutputStream(""));
//完成文件复制到某个文件夹下
FileUtils.copyFileToDirectory(new File(""),new File(""));
//完成文件夹复制到某个文件夹下
FileUtils.copyDirectoryToDirectory(new File(""),new File(""));
//完成删除文件夹
FileUtils.deleteDirectory(new File(""));
//java自己也有一些一行代码完成复制的操作
Files.copy(Paths.get(""),Paths.get(""));
线程
概述
多线程的创建
方式一:继承Thread类
//1、定义一个线程类继承Thread
class MyThread extends Thread {
//2、重写run方,里面是定义线程以后干什么
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程启动" + i);
}
}
}
public static void main(String[] args) {
//3、new一个线程对象
Thread t = new MyThread();//多态
//4、调用start方法启动线程,其实是调用run方法
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程启动" + i);
}
}
方式二:实现Runnable接口
//1、定义一个线程任务类 实现Runnable接口
class MyRunnable implements Runnable {
//2、重写run方法,定义线程的执行任务
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程输出" + i);
}
}
}
public static void main(String[] args) {
//3、创建一个任务对象
Runnable target = new MyRunnable();//多态
//4、把任务对象交给Thread处理
Thread t = new Thread(target);
//5、启动线程
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出" + i);
}
}
public static void main(String[] args) {
//内部类写法
Runnable target = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程1输出" + i);
}
}
};
Thread t = new Thread(target);
t.start();
//简化写法
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程2输出" + i);
}
}
}).start();
//再简化
new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("子线程3输出" + i);
}
}).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程输出" + i);
}
}
方式三:实现Callable接口----可返回线程的执行结果
//1、定义一个任务类 实现Callable接口
class MyCallable implements Callable<String> {
//创建一个有参构造器
private int n;
public MyCallable(int n) {
this.n = n;
}
//重写call方法(任务方法)
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i <=n ; i++) {
sum += i;
}
return "子线程输出结果" + sum;
}
}
public static void main(String[] args) {
//3、创建Callable任务对象
Callable<String> call = new MyCallable(100);//没法直接交给Thread
//4、把Callable任务对象 交给 FutureTask 对象
//FutureTask对象的作用1:使Runnable的对象(实现Runnable接口),可以交给Thread
//FutureTask对象的作用2:可以在线程执行完毕之后调用其get方法得到线程执行的结果
FutureTask<String> f = new FutureTask<>(call);
//5、交给线程处理
Thread t = new Thread(f);
t.start();//启动
try {
//如果f任务没有执行完毕,这里的代码会等到线程执行完毕才启动
//获取线程执行结果
String rs = f.get();
System.out.println("结果为" + rs);
} catch (Exception e) {
e.printStackTrace();
}
}
Thread的常用方法
public static void main(String[] args) {
Thread t1 = new MyThread();
t1.setName("1号");
t1.start();
System.out.println(t1.getName());
Thread t2 = new MyThread();
t2.setName("2号");
t2.start();
System.out.println(t2.getName());
Thread m = Thread.currentThread();
System.out.println(m.getName());
for (int i = 0; i < 5; i++) {
System.out.println("main线程输出" + i);
}
}
//3、创建一个任务对象
Runnable target = new MyRunnable();
//4、把任务对象交给Thread处理
Thread t = new Thread(target);
Thread t = new Thread(target,"1号");
//创建一个线程类
public class MyThread extends Thread{
public MyThread() {
}
//创建一个有参构造器
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "输出" + i);
}
}
}
Thread t1 = new MyThread("1号");
// t1.setName("1号");
t1.start();
System.out.println(t1.getName());
public static void main(String[] args) throws Exception {
for (int i = 1; i <= 5; i++) {
System.out.println("输出" + i);
if (i == 3){
//让当前代码进入休眠状态
Thread.sleep(3000);
}
}
}
线程安全
public class Account {
private double money;
public Account() {
}
public Account(double money) {
this.money = money;
}
public void drawMoney(double money) {
//先判断是谁来取钱
String name = Thread.currentThread().getName();
//判断余额是否充足
if (this.money >= money){
System.out.println(name + "来取" + money + "元");
//更新余额
this.money -= money;
System.out.println("剩余" + this.money);
}else{
System.out.println("余额不足");
}
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
public class DrawThread extends Thread{
private Account acc;
public DrawThread(Account acc , String name){
super(name);
this.acc = acc;
}
@Override
public void run() {
//取钱
acc.drawMoney(100000);
}
}
public static void main(String[] args) {
//创建一个公共账户对象
Account acc = new Account(100000);
//创建两个线程
new DrawThread(acc,"小明").start();
new DrawThread(acc,"小红").start();
}
线程同步
概述
方式一:同步代码块
//同步代码块
synchronized ("heima") {
//判断余额是否充足
if (this.money >= money){
System.out.println(name + "来取" + money + "元");
//更新余额
this.money -= money;
System.out.println(name + "取钱后剩余" + this.money);
}else{
System.out.println(name + "来取,余额不足");
}
}
//同步代码块
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) {
}
方式三:Lock锁
//使用final修饰:唯一且不可变
private final Lock lock = new ReentrantLock();
lock.lock();//上锁
try {
if (this.money >= money) {
System.out.println(name + "来取" + money + "元");
//更新余额
this.money -= money;
System.out.println(name + "取钱后剩余" + this.money);
} else {
System.out.println(name + "来取,余额不足");
}
} finally {
//使用finally,避免上面代码出bug,无法执行解锁操作
lock.unlock();//解锁
}
线程通信
案例
public static void main(String[] args) {
Account acc = new Account("123",0);
new Draw(acc,"小明").start();
new Draw(acc,"小红").start();
new Deposit(acc,"亲爹").start();
new Deposit(acc,"干爹").start();
new Deposit(acc,"岳父").start();
}
public class Account {
private String idName;
private double money;
public Account() {
}
public Account(String idName, double money) {
this.idName = idName;
this.money = money;
}
//存钱
public synchronized void deposit(double money) {
try {
String name = Thread.currentThread().getName();
if (this.money == 0){
//存钱
this.money += money;
System.out.println(name + "存了" + money + "剩余" + this.money);
this.notifyAll();//唤醒所有线程
this.wait();//锁对象,让当前程序进入等待
}else {
//不存
this.notifyAll();//唤醒所有线程
this.wait();//锁对象,让当前程序进入等待
}
} catch (Exception e) {
e.printStackTrace();
}
}
//取钱
public synchronized void drawMoney(double money) {
try {
String name = Thread.currentThread().getName();
if (this.money >= money){
//取钱
this.money -= money;
System.out.println(name + "取走了" + money + "剩余" + this.money);
//没钱了
this.notifyAll();//唤醒所有线程
this.wait();//锁对象,让当前程序进入等待
}else {
this.notifyAll();//唤醒所有线程
//唤醒别人,等待自己
this.wait();//锁对象,让当前程序进入等待
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
}
public class Draw extends Thread{
private Account acc;
public Draw (Account acc , String name ){
super(name);
this.acc = acc;
}
@Override
public void run() {
//取钱
while (true) {
acc.drawMoney(100000);
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class Deposit extends Thread{
private Account acc;
public Deposit(Account acc , String name ){
super(name);
this.acc = acc;
}
@Override
public void run() {
//取钱
while (true) {
acc.deposit(100000);
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
线程池
概述
线程池实现的API,参数说明
//参数一:主线程个数
//参数二:主线程和临时线程总个数
//参数三:临时线程存在时间
//参数四:时间单位(TimeUnit.SECONDS)
//参数五:队列中线程可以等待的个数new ArrayBlockingQueue<>(2)
//参数六:照写Executors.defaultThreadFactory()
//参数七:新任务拒绝策略,默认new ThreadPoolExecutor.AbortPolicy()
ExecutorService pool = new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
参数七可选择的拒绝策略
线程池处理Runnable任务
//实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "线程输出" + i);
}
try {
System.out.println(Thread.currentThread().getName() + "与任务线程绑定");
Thread.sleep(100000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//创建线程池对象
/** int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler
* */
ExecutorService pool = new ThreadPoolExecutor(3,5,6,TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//创建Runnable任务对象
Runnable target = new MyRunnable();
//线程池执行Runnable任务
//主线程
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.execute(target);
// pool.execute(target);
//关闭线程池(开发中一般不使用)
pool.shutdownNow();//立即关闭,即使任务没有完成。会丢失任务
pool.shutdown();//会等任务完成后再关闭(建议使用)
}
线程池处理Callable任务
//实现Callable接口,可以返回线程执行结果
public class MyCallable implements Callable {
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() + "和为" + sum;
}
}
public static void main(String[] args) throws Exception {
ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(5),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//把Callable任务交给线程池处理
//Callable target = new MyCallable(100);
//Future<String> f1 = pool.submit(target);
Future<String> f1 = pool.submit(new MyCallable(100));
//得到线程执行结果
//String rs = f1.get();
//System.out.println(rs);
System.out.println(f1.get());
}
Executors工具类实现线程池
public static void main(String[] args) {
//创建固定线程数量的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
//线程池执行Runnable任务
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());
pool.execute(new MyRunnable());//已经没有多余线程
}
定时器
方式一:Timer
public static void main(String[] args) {
//创建Timer定时器
Timer timer = new Timer();
//调用方法处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行一次");
try {
Thread.sleep(10000);//如果前一个线程睡眠,会影响后面线程的执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},3000,2000);//等待多久开始,此后每次间隔多久
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行一次");
}
},3000,2000);
}
方式二:ScheduledExecutorService----更常用
public static void main(String[] args) {
//创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
//开启定时任务
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行AAA");
try {
Thread.sleep(10000);//即使前一个线程睡眠,也不会影响后面线程的执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},0,2, TimeUnit.SECONDS);//开始时间,间隔时间,时间单位
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行BBB");
}
},0,2, TimeUnit.SECONDS);
}
线程并发、并行
线程的生命周期
网络编程
网络通信三要素
IP地址
IP地址操作类–InetAddress
//获取本机地址对象
InetAddress ip1 = InetAddress.getLocalHost();
System.out.println(ip1);
System.out.println(ip1.getHostAddress());
System.out.println(ip1.getHostName());
//根据域名获取ip对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com");
System.out.println(ip2.getHostAddress());
System.out.println(ip2.getHostName());
//获取公网ip对象
InetAddress ip3 = InetAddress.getByName("39.156.66.14");
System.out.println(ip3.getHostAddress());
System.out.println(ip3.getHostName());
//判断是否联通
System.out.println(ip3.isReachable(5000));
端口号
协议
UDP通信
快速入门----一发一收
一发一收
public static void main(String[] args) throws Exception {
System.out.println("============客户端=============");
//创建发送端对象,发送端自带端口
DatagramSocket socket = new DatagramSocket();
//创建一个数据包对象封装数据
/*
public DatagramPacket(byte buf[], int length,
InetAddress address, int port)
参数一;要发送的数据字节数组
参数二:要发送的数组长度
参数三:接收端的主机IP地址
参数四:接收端的端口
*/
byte [] buffer = "我是快乐的小韭菜。".getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length,InetAddress.getLoopbackAddress(),8888);
//发送数据
socket.send(packet);
//关闭管道
socket.close();
}
public static void main(String[] args) throws Exception {
System.out.println("========服务端=============");
//创建一个接收端对象,要自己声明端口
DatagramSocket socket = new DatagramSocket(8888);
//创建一个数据包对象接收数据
byte [] buffer = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
//等待接收数据
socket.receive(packet);
//取出数据
//接到多少,输出多少
int len = packet.getLength();
String rs = new String(buffer,0,len);
System.out.println("输出:"+ rs);
//获取发送端的ip和端口
String ip = packet.getAddress().toString();
System.out.println("发送端的ip:" + ip);
int port = packet.getPort();
System.out.println("发送端的端口:" + port);
//关闭管道
socket.close();
}
多发多收
在一发一收基础上,增加死循环实现多发多收
public static void main(String[] args) throws Exception {
System.out.println("============客户端=============");
//创建发送端对象,发送端自带端口
DatagramSocket socket = new DatagramSocket();
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[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getLoopbackAddress(), 8888);
//发送数据
socket.send(packet);
}
}
public static void main(String[] args) throws Exception {
System.out.println("========服务端=============");
//创建一个接收端对象,要自己声明端口
DatagramSocket socket = new DatagramSocket(8888);
//创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
//等待接收数据
socket.receive(packet);
//取出数据
//接到多少,输出多少
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.out.println("收到来自:" + packet.getAddress().toString() + ",对方端口是:" + packet.getPort() + "的消息:" + rs);
}
}
广播、组播
广播
在多发多收基础上,改变接收端的接收ip地址
public static void main(String[] args) throws Exception {
System.out.println("============客户端=============");
//创建发送端对象,发送端自带端口
DatagramSocket socket = new DatagramSocket();
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[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length,
InetAddress.getByName("255.255.255.255"), 9999);
//发送数据
socket.send(packet);
}
}
public static void main(String[] args) throws Exception {
System.out.println("========服务端=============");
//创建一个接收端对象,要自己声明端口
DatagramSocket socket = new DatagramSocket(9999);
//创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
//等待接收数据
socket.receive(packet);
//取出数据
//接到多少,输出多少
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.out.println("收到来自:" + packet.getAddress().toString() + ",对方端口是:" + packet.getPort() + "的消息:" + rs);
}
}
组播
在多发多收基础上,为服务端创建组播ip
System.out.println("============客户端=============");
//创建发送端对象,发送端自带端口
DatagramSocket socket = new DatagramSocket();
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[] buffer = msg.getBytes();
DatagramPacket packet = new DatagramPacket(buffer, buffer.length,
InetAddress.getByName("224.0.1.1"), 9999);
//发送数据
socket.send(packet);
}
}
public static void main(String[] args) throws Exception {
System.out.println("========服务端=============");
//创建一个接收端对象,要自己声明端口
MulticastSocket socket = new MulticastSocket(9999);
//把当前接收端加入到一个组播当中去,绑定对应的组播消息的组播ip
// socket.joinGroup(InetAddress.getByName("224.0.1.1"));
socket.joinGroup(new InetSocketAddress(InetAddress.getByName("224.0.1.1"),9999),
NetworkInterface.getByInetAddress(InetAddress.getLocalHost()));
//创建一个数据包对象接收数据
byte[] buffer = new byte[1024 * 64];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while (true) {
//等待接收数据
socket.receive(packet);
//取出数据
//接到多少,输出多少
int len = packet.getLength();
String rs = new String(buffer, 0, len);
System.out.println("收到来自:" + packet.getAddress().toString() + ",对方端口是:" + packet.getPort() + "的消息:" + rs);
}
}
TCP通信
快速入门
客户端
public static void main(String[] args) {
System.out.println("==========客户端============");
try {
//创建Socket通信管道请求服务端的连接
// public Socket(String host, int port)
//参数一:服务端的ip地址
//参数二:服务端的端口
Socket socket = new Socket("127.0.0.1" , 7777);
//从Socket通信管道中得到一个字节输出流,负责发送消息
OutputStream os = socket.getOutputStream();
//把低级字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
ps.print("发送TCP消息");
ps.flush();//刷新
//一般不用关闭管道,直接关闭会导致通信失败
//socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
服务端
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(7777);
//调用accept方法,等待客户端发送Socket连接请求,建立连接
Socket socket = serverSocket.accept();
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
if ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
多发多收
public static void main(String[] args) {
System.out.println("==========客户端============");
try {
//创建Socket通信管道请求服务端的连接
// public Socket(String host, int port)
//参数一:服务端的ip地址
//参数二:服务端的端口
Socket socket = new Socket("10.31.107.20", 7777);
//从Socket通信管道中得到一个字节输出流,负责发送消息
OutputStream os = socket.getOutputStream();
//把低级字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("请输入:");
String msg = sc.nextLine();
//发消息
ps.println(msg);
ps.flush();
}
//一般不用关闭管道,直接关闭会导致通信失败
//socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(7777);
//调用accept方法,等待客户端发送Socket连接请求,建立连接
Socket socket = serverSocket.accept();
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
同时接收多个客户端消息
public static void main(String[] args) {
System.out.println("==========客户端============");
try {
//创建Socket通信管道请求服务端的连接
// public Socket(String host, int port)
//参数一:服务端的ip地址
//参数二:服务端的端口
Socket socket = new Socket("10.31.107.20", 7777);
//从Socket通信管道中得到一个字节输出流,负责发送消息
OutputStream os = socket.getOutputStream();
//把低级字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("请输入:");
String msg = sc.nextLine();
//发消息
ps.println(msg);
ps.flush();
}
//一般不用关闭管道,直接关闭会导致通信失败
//socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(7777);
//定义一个死循环由主线程负责,不断的接收客户端的Socket管道连接
while (true) {
//每接收到一个客户端的管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
//创建独立线程处理socket
new ServerReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
}
}
}
使用线程池优化
public static void main(String[] args) {
System.out.println("==========客户端============");
try {
//创建Socket通信管道请求服务端的连接
// public Socket(String host, int port)
//参数一:服务端的ip地址
//参数二:服务端的端口
Socket socket = new Socket("10.31.107.20", 7777);
//从Socket通信管道中得到一个字节输出流,负责发送消息
OutputStream os = socket.getOutputStream();
//把低级字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("请输入:");
String msg = sc.nextLine();
//发消息
ps.println(msg);
ps.flush();
}
//一般不用关闭管道,直接关闭会导致通信失败
//socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//创建一个静态线程池
private static ExecutorService pool = new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(7777);
//定义一个死循环由主线程负责,不断的接收客户端的Socket管道连接
while (true) {
//每接收到一个客户端的管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "上线了");
//想要使用线程池,必须封装成任务对象,线程才能执行
//把socket管道封装成任务对象
Runnable target = new ServerReaderRunnable(socket);
//线程池执行任务
pool.execute(target);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable (Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
}
}
}
及时通信
public class Demo1 {
public static void main(String[] args) {
System.out.println("==========客户端============");
try {
//创建Socket通信管道请求服务端的连接
// public Socket(String host, int port)
//参数一:服务端的ip地址
//参数二:服务端的端口
Socket socket = new Socket("10.31.107.20", 7777);
//创建一个独立线程专门负责接收消息
new ClientReaderThread(socket).start();
//从Socket通信管道中得到一个字节输出流,负责发送消息
OutputStream os = socket.getOutputStream();
//把低级字节输出流包装成打印流
PrintStream ps = new PrintStream(os);
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入:");
String msg = sc.nextLine();
//发消息
ps.println(msg);
ps.flush();
}
//一般不用关闭管道,直接关闭会导致通信失败
//socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ClientReaderThread extends Thread {
private Socket socket;
public ClientReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
while ((msg = br.readLine()) != null) {
System.out.println( "收到消息:" + msg);
}
} catch (Exception e) {
System.out.println("服务端把你踢出群聊");
}
}
}
public class serverDemo {
//定义一个静态的集合,储存所有在线的管道
public static List<Socket> AllOnlineSockets = new ArrayList<>();
//创建一个静态线程池
private static ExecutorService pool = new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(7777);
//定义一个死循环由主线程负责,不断的接收客户端的Socket管道连接
while (true) {
//每接收到一个客户端的管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress() + "上线了");
AllOnlineSockets.add(socket);//管道上线
//把socket管道封装成任务对象
Runnable target = new ServerReaderRunnable(socket);
pool.execute(target);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ServerReaderRunnable implements Runnable{
private Socket socket;
public ServerReaderRunnable (Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
//从Socket通信管道中得到一个字节输入流,接收信息
InputStream is = socket.getInputStream();
//把字节输入流包装成缓冲字符输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//按照行读取消息
String msg;
while ((msg = br.readLine()) != null){
System.out.println(socket.getRemoteSocketAddress() + "说了:" + msg);
//把消息进行端口转发给全部客户端socket管道
sendMsgToAll(msg);
}
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
serverDemo.AllOnlineSockets.remove(socket);
}
}
private void sendMsgToAll(String msg) throws Exception {
for (Socket socket : serverDemo.AllOnlineSockets) {
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(msg);
ps.flush();
}
}
}
模拟BS系统
public class BSserverDemo {
public static void main(String[] args) {
System.out.println("==========服务端==============");
try {
//注册服务端端口
ServerSocket serverSocket = new ServerSocket(8080);
//定义一个死循环由主线程负责,不断的接收客户端的Socket管道连接
while (true) {
//每接收到一个客户端的管道,交给一个独立的子线程负责读取消息
Socket socket = serverSocket.accept();
//创建独立线程处理socket
new BSserverReaderThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class BSserverReaderThread extends Thread {
private Socket socket;
public BSserverReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//浏览器已经与本线程建立Socket管道
//响应消息给浏览器显示
PrintStream ps = new PrintStream(socket.getOutputStream());
//必须响应HTTP协议格式数据,否则浏览器不认识消息
ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的消息
ps.println("Content-Type:text/html;charset=UTF-8");//响应的数据类型 文本/网页
ps.println();//必须发送一个空行
//响应数据正文
ps.println("<span style='color:red;font-size:90px'> 《傻逼阿康》 </span>");
ps.close();
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
}
}
}
//引入线程池优化
public class BSserverDemo {
//创建一个静态线程池
private static ExecutorService pool = new ThreadPoolExecutor(3,5,6,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
public static void main(String[] args) {
try {
//注册服务端端口
ServerSocket ss = new ServerSocket(8080);
//定义一个死循环由主线程负责,不断接收客户端的Socket管道连接
while (true) {
//每接收到一个客户端的管道,交给一个独立的子线程负责读取消息
Socket socket = ss.accept();
//创建独立线程处理socket
pool.execute(new ServerReaderRunnable(socket));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class ServerReaderRunnable implements Runnable {
private Socket socket;
public ServerReaderRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
//浏览器已经与本线程建立Socket管道
//响应消息给浏览器显示
PrintStream ps = new PrintStream(socket.getOutputStream());
//必须响应HTTP协议格式数据,否则浏览器不认识消息
ps.println("HTTP/1.1 200 OK");//协议类型和版本 响应成功的消息
ps.println("Content-Type:text/html;charset=UTF-8");//响应的数据类型 文本/网页
ps.println();//必须发送一个空行
//响应数据正文
ps.println("<span style='color:red;font-size:90px'> 《傻逼阿康》 </span>");
ps.close();
} catch (Exception e) {
System.out.println(socket.getRemoteSocketAddress() + "下线了");
}
}
}
单元测试
概述
快速入门
public class Server {
public String loginName(String loginName , String passWord){
if("admin".equals(loginName) && "123456".equals(passWord)){
return "登录成功";
}else {
return "有误";
}
}
public void selectName(){
System.out.println(10/0);
System.out.println("成功");
}
}
/*
测试方法:
1.必须是公开的 无参数 无返回值的方法
2.测试方法必须使用Test注解标记
*/
//有参数方法测试
@Test
public void test(){
Server server = new Server();
String rs = server.loginName("admin" , "123456");
//进行预期结束的正确性测试,断言
Assert.assertEquals("业务登录成功" , "登录成功" ,rs);
}
//无参数方法测试
@Test
public void selectServer(){
Server server = new Server();
server.selectName();
}
常用注解
反射
概述
获取类对象
public class Test {
public static void main(String[] args) throws Exception {
//1、Class类中的一个静态方法:forName(全限名:包名+类名)
Class c = Class.forName("pk1.Test");
System.out.println(c);
//2、类名.Class//最常用
Class c1 = Test.class;
System.out.println(c1);
//3、对象.getClass() 获取对象对应类的Class对象
Test t = new Test();
Class c2 = t.getClass();
System.out.println(c2);
}
}
获取构造器对象
public class Student {
private int age ;
private String name;
public Student() {
System.out.println("无参");
}
public Student(int age, String name) {
System.out.println("有参");
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//1、getConstructors()
//获取全部构造器,只能获取public修饰的构造器
@Test
public void getConstructors() {
//a.获取类对象
Class c = Student.class;
//b.提取类中全部由public修饰的构造器对象
Constructor[] constructors = c.getConstructors();
//c.遍历所有构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "=====>" + constructor.getParameterCount());//得到参数名称和对应的参数个数
}
}
//2、getDeclaredConstructors()
//获取全部构造器,无所谓权限
@Test
public void getDeclaredConstructors() {
//a.获取类对象
Class c = Student.class;
//b.提取类中全部的构造器对象
Constructor[] constructors = c.getDeclaredConstructors();
//c.遍历所有构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "=====>" + constructor.getParameterCount());
}
}
//3、getConstructor(Class<?>... parameterTypes)
//获取某个构造器,只能拿public修饰的某个构造器
@Test
public void getConstructor() throws Exception {
//a.获取类对象
Class c = Student.class;
//b.定位当个构造器(按照参数定位无参构造器)
Constructor cons = c.getConstructor();
System.out.println(cons.getName() + "=====>" + cons.getParameterCount());
}
//4、getDeclaredConstructor(Class<?>... parameterTypes)
//获取某个构造器,无所谓权限
@Test
public void getDeclaredConstructor() throws Exception {
//a.获取类对象
Class c = Student.class;
//b.定位当个构造器(按照参数定位无参构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "=====>" + cons.getParameterCount());
//c.定位某个有参构造器//要知道参数变量类型
Constructor cons1 = c.getDeclaredConstructor(int.class , String.class);
System.out.println(cons1.getName() + "=====>" + cons1.getParameterCount());
}
//1、调用构造器得到一个类的对象返回
@Test
public void getDeclaredConstructor() throws Exception {
//a.获取类对象
Class c = Student.class;
//b.定位当个构造器(按照参数定位无参构造器)
Constructor cons = c.getDeclaredConstructor();
System.out.println(cons.getName() + "=====>" + cons.getParameterCount());
// //如果遇到构造器被私有,可以暴力反射
// cons.setAccessible(true);//权限被打开
Student s = (Student) cons.newInstance();
System.out.println(s);
System.out.println("--------------------");
//c.定位某个有参构造器
Constructor cons1 = c.getDeclaredConstructor(int.class , String.class);
System.out.println(cons1.getName() + "=====>" + cons1.getParameterCount());
Student s1 = (Student) cons1.newInstance(18,"孙悟空");
System.out.println(s1);
}
获取成员变量对象
//1、获取全部的成员变量
// File[] getDeclaredFields()
@Test
public void getDeclaredFields() {
//a.获取类对象
Class c = Student.class;
//b.定位所有成员变量
Field[] fields = c.getDeclaredFields();
//c.遍历一下
for (Field field : fields) {
System.out.println(field.getName() + "====>" + field.getType());//得到成员变量的名称和类型
}
}
//2、获取某个成员变量getDeclaredField(String name)
@Test
public void getDeclaredField() throws Exception {
//a.获取类对象
Class c = Student.class;
//b.定位某个成员变量
Field f = c.getDeclaredField("age");//申明变量的名称
System.out.println(f.getName() + "====>" + f.getType());
}
@Test
public void setField() throws Exception {
//a.获取类对象
Class c = Student.class;
//提取某个成员变量
Field ageF = c.getDeclaredField("age");
ageF.setAccessible(true);//暴力打开权限
//c.赋值
Student s = new Student();
ageF.set(s,18);//s.setAge(18)
System.out.println(s);
//d.取值
int age = (int) ageF.get(s);//s.getAge
System.out.println(age);
}
获取方法对象
//1、获取类中所有成员方法对象
@Test
public void getDeclaredMethods(){
//a.获取类对象
Class c = Dog.class;
//b.获取全部方法,包括私有
Method[] methods = c.getDeclaredMethods();
//c.遍历全部方法
for (Method method : methods) {
System.out.println(method.getName() + "返回类型:" + method.getReturnType() + "参赛名称:" + method.getParameterCount()) ;
}
}
//2、获取某个方法对象
@Test
public void getDeclaredMethod() throws Exception {
//a.获取类对象
Class c = Dog.class;
//b.提取单个方法对象
Method m1 = c.getDeclaredMethod("eat");//要申明方法名称//无参对象
Method m2 = c.getDeclaredMethod("eat",String.class);//有参对象
//暴力打开权限
m1.setAccessible(true);
m2.setAccessible(true);
//c.触发方法的执行
Dog d = new Dog();
//注意:方法如果没有返回结果回来,那么返回的是null
String result = (String) m1.invoke(d);
System.out.println(result);
String result2 = (String) m2.invoke(d,"骨头");
System.out.println(result2);
}
反射的作用
绕过编译阶段为集合添加数据
ArrayList<Integer> list = new ArrayList<>();
list.add(12);
list.add(23);
Class c = list.getClass();
Method add = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean) add.invoke(list,"黑马");
System.out.println(rs);
System.out.println(list);
通用框架的底层原理
public class MybatisUtil {
//保存任意类型变量
public static void save(Object obj){
try (
//获取打印输出管道,使用完可自动释放资源
PrintStream ps = new PrintStream(new FileOutputStream("data",true));
){
//获取类对象
Class c = obj.getClass();
ps.println("=========" + c.getSimpleName() + "==============");
//获取全部成员变量
Field[] f = c.getDeclaredFields();
//遍历获取成员变量的信息
for (Field field : f) {
//获取成员变量的名称
String name = field.getName();
//提取本成员变量在obj对象中的值(取值)
field.setAccessible(true);//暴力打开权限
String value = field.get(obj) + "";//把该值转为字符串类型
//将成员变量名称和对应的值输出
ps.println(name + "=" + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Student s = new Student();
s.setName("小红");
s.setAge(18);
s.setSex('男');
s.setClassName("12");
s.setHobby("游泳");
MybatisUtil.save(s);
Teacher t = new Teacher();
t.setName("王艳梅");
t.setSex('男');
t.setSalary(6000);
MybatisUtil.save(t);
}
注解
概述
自定义注解
public @interface book {
String name();
String[] authors();
double price();
}
@book(name="Java",authors = {"黑马"},price = 100)
public class Demo {
@book(name="Java",authors = {"黑马"},price = 100)
public static void main(String[] args) {
}
}
public @interface MyBook {
String value();//特殊属性
}
@MyBook(value = "JavaSE")
@MyBook("JavaSE")
public class Demo {
}
元注解
@Target({ElementType.METHOD,ElementType.FIELD})//元注解
@Retention(RetentionPolicy.RUNTIME)//一直活着,在运行阶段也不消失
public @interface MyTest {
}
注解解析
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface book {
String value();
String[] authors();
double price();
}
public class Demo2 {
@Test
public void parseClass(){
//a.得到类对象
Class c = BookStore.class;
//b.判断这个类上是否存在注解
if (c.isAnnotationPresent(book.class)){
//c.直接获取该注解对象
book books = (book) c.getDeclaredAnnotation(book.class);
System.out.println(books.value());
System.out.println(Arrays.toString(books.authors()));
System.out.println(books.price());
}
}
@Test
public void parseMethod() throws Exception {
//a.得到类对象
Class c = BookStore.class;
//获取类方法对象
Method m = c.getDeclaredMethod("test");
//b.判断这个类上是否存在注解
if (m.isAnnotationPresent(book.class)){
//c.直接获取该注解对象
book books = (book) m.getDeclaredAnnotation(book.class);
System.out.println(books.value());
System.out.println(Arrays.toString(books.authors()));
System.out.println(books.price());
}
}
}
@book(value = "活着",authors = "余华" ,price = 12)
class BookStore{
@book(value = "酒国",authors = "莫言" ,price = 112)
public void test(){
}
}
junit框架
@Target({ElementType.METHOD})//元注解
@Retention(RetentionPolicy.RUNTIME)//一直活着,在运行阶段也不消失
public @interface MyTest {
}
public class Demo3 {
public void test1() {
System.out.println("=====test1======");
}
@MyTest
public void test2() {
System.out.println("=====test2======");
}
@MyTest
public void test3() {
System.out.println("=====test3======");
}
public static void main(String[] args) throws Exception {
Demo3 d = new Demo3();
//a.获取类对象
Class c = Demo3.class;
//获取所有方法对象
Method[] m = c. getDeclaredMethods();
//遍历所有方法,判断是否有注解
for (Method method : m) {
if (method.isAnnotationPresent(MyTest.class)){
method.invoke(d);
}
}
}
}
动态代理
概述
快速入门
//接口
public interface Skill {
void dance();
void sing();
}
//被代理对象实现接口
public class Star implements Skill{
private String name;
public Star(String name) {
this.name = name;
}
@Override
public void dance() {
System.out.println(name + "跳得好");
}
@Override
public void sing() {
System.out.println(name + "唱得好");
}
}
public class StarProxyInstance {
//设计一个方法来返回一个明星对象的代理对象
public static Skill getProxy(Star obj){
//为这个对象生成一个代理对象
/*
newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
*/
return (Skill) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收首付款");
//method 调用方法对象 args 代表这个方法的参数
Object rs = method.invoke(obj,args);
System.out.println("收尾款");
return rs;
}
});
}
}
public static void main(String[] args) {
//创建一个对象
Star s = new Star("杨超越");
//为对象创建一个代理对象
Skill s2 = StarProxyInstance.getProxy(s);
s2.dance();
s2.sing();
}
动态代理的应用案例:性能分析
public interface UserService {
String login(String loginName,String passWord);
void deleteUser();
String selectUser();
void deleteById(int id);
}
public class UserServiceImpl implements UserService {
@Override
public void deleteById(int id) {
try {
System.out.println("删除了" + id);
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ProxyUnit {
//通过一个静态方法,为用户业务对象返回一个代理对象
public static UserService getProxy(UserService obj){
return (UserService) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
//真正触发执行
Object rs = method.invoke(obj,args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s");
return rs;
}
});
}
}
public static void main(String[] args) {
//把用户对象包装成代理对象
UserService userService = ProxyUnit.getProxy(new UserServiceImpl());
System.out.println(userService.login("admin", "12345"));
System.out.println(userService.selectUser());
userService.deleteUser();
userService.deleteById(4);
}
public class ProxyUnit {
//使用泛型,可以为任意接口类型的实现类对象做代理
public static <T> T getProxy(T obj){
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
//真正触发执行
Object rs = method.invoke(obj,args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s");
return rs;
}
});
}
}
XML
概述
XML的创建和语法规则
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 注释 -->
<student>
<name>女儿国国外</name>
<sex>女</sex>
<hobby>唐僧</hobby>
<info>
<age>18</age>
<adder>女儿国</adder>
</info>
<sql>
select * from user where age < 18;
<![CDATA[
select * from user where age < 18;
]]>
</sql>
</student>
XML文档约束
方式一:DTD约束
//DTD约束文档格式
<!ELEMENT 书架 (书+)>
<!ELEMENT 书 (书名,作者,售价)>
<!ELEMENT 书名 (#PCDATA)>
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
//调用约束文档
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 书架 SYSTEM "data.dtd">
<书架>
<书>
<书名></书名>
<作者></作者>
<售价></售价>
</书>
</书架>
方式二:schema约束
XML解析技术
概述
Dom4J解析XML文件
public class Demo {
@Test
public void parseXMLData() throws Exception {
//1、创建一个Dom4j解析器对象,代表整个Dom4j框架
SAXReader saxReader = new SAXReader();
//2、把XML文件加载到内存中成为一个Document文件对象
// Document document = saxReader.read(new File("xml\\xmldata\\hello_world.xml"));//需要通过模块名去定位
//注意:getResourceAsStream中的/是直接从src目录下拿文件
InputStream is = Demo.class.getResourceAsStream("/hello_world.xml");
Document document = saxReader.read(is);
//3、获取根元素对象
Element root = document.getRootElement();
System.out.println(root.getNamespacePrefix());
}
}
Dom4J解析XML文件中的各种节点
案例
<?xml version="1.0" encoding="UTF-8" ?>
<contactList>
<contact id="1" vip="true">
<name> 潘金莲 </name>
<gender>女</gender>
<email>panpan@itcast.cn</email>
</contact>
<contact id="2" vip="false">
<name>武松</name>
<gender>男</gender>
<email>wusong@itcast.cn</email>
</contact>
<contact id="3" vip="false">
<name>武大郎</name>
<gender>男</gender>
<email>wuda@itcast.cn</email>
</contact>
<user>
</user>
</contactList>
public class Contact {
private String name;
private int id;
private boolean vip;
private char gender;
private String email;
public Contact() {
}
public Contact(String name, int id, boolean vip, char gender, String email) {
this.name = name;
this.id = id;
this.vip = vip;
this.gender = gender;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isVip() {
return vip;
}
public void setVip(boolean vip) {
this.vip = vip;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Contact{" +
"name='" + name + '\'' +
", id=" + id +
", vip=" + vip +
", gender=" + gender +
", email='" + email + '\'' +
'}';
}
}
public class Demo {
@Test
public void parseToList() throws Exception {
//1、导入框架
//2、创建SaxReader对象
SAXReader saxReader = new SAXReader();
//3、加载XML文件成为文档对象Document对象
Document document = saxReader.read(Demo.class.getResourceAsStream("/Contacts.xml"));
//4、先拿根元素
Element root = document.getRootElement();
//5、提取contact子元素
List<Element> contactElms = root.elements("contact");
//6、准备一个ArrayList集合封装联系人信息
List<Contact> contacts = new ArrayList<>();
//7、遍历Contact子元素
for (Element contactElm : contactElms) {
//8、每一个子元素都是一个联系人对象
Contact contact = new Contact();
contact.setId(Integer.valueOf(contactElm.attributeValue("id")));
contact.setVip(Boolean.valueOf(contactElm.attributeValue("vip")));
contact.setName(contactElm.elementTextTrim("name"));
contact.setGender(contactElm.elementTextTrim("gender").charAt(0));
contact.setEmail(contactElm.elementText("email"));
//9、把联系人对象的数据加到List集合
contacts.add(contact);
}
//10、遍历List集合
for (Contact contact : contacts) {
System.out.println(contact);
}
}
}
XML检索技术:Xpath
绝对路径
public class XpathDemo {
//1、绝对路径:根元素/子元素/子元素
@Test
public void parse01() throws Exception {
//a.创建解析器对象
SAXReader saxReader = new SAXReader();
//b.把XML加载成Document对象
Document document =
saxReader.read(XpathDemo.class.getResourceAsStream("/Contacts2.xml"));
//c.检索全部名称
List<Node> nameNodes = document.selectNodes("contactList/contact/name");
for (Node nameNode : nameNodes) {
Element nameEle = (Element) nameNode;
System.out.println(nameEle.getTextTrim());
}
}
}
相对路径
public class XpathDemo {
//2、相对路径:./子元素/子元素 (.代表当前路径)
@Test
public void parse02() throws Exception {
//a.创建解析器对象
SAXReader saxReader = new SAXReader();
//b.把XML加载成Document对象
Document document =
saxReader.read(XpathDemo.class.getResourceAsStream("/Contacts2.xml"));
Element root = document.getRootElement();
//c.检索全部名称
List<Node> nameNodes = root.selectNodes("./contact/name");
for (Node nameNode : nameNodes) {
Element nameEle = (Element) nameNode;
System.out.println(nameEle.getTextTrim());
}
}
}
全文搜索
public class XpathDemo {
/*
3、全文搜索:
//元素 在全文找这个元素
//元素1/元素2 在全文找元素1下面的一级元素2
//元素1//元素2 在全文找元素1下面的全部元素2
*/
@Test
public void parse03() throws Exception {
//a.创建解析器对象
SAXReader saxReader = new SAXReader();
//b.把XML加载成Document对象
Document document =
saxReader.read(XpathDemo.class.getResourceAsStream("/Contacts2.xml"));
//c.检索数据
// List<Node> nameNodes = document.selectNodes("//name");
// List<Node> nameNodes = document.selectNodes("//contact/name");
List<Node> nameNodes = document.selectNodes("//contact//name");
for (Node nameNode : nameNodes) {
Element nameEle = (Element) nameNode;
System.out.println(nameEle.getTextTrim());
}
}
}
属性查找
public class XpathDemo {
/*
4、属性查找
//@属性名称 在全文检索属性对象
//元素[@属性名称] 在全文检索包含该属性的元素对象
//元素[@属性名称=值] 在全文检索包含该属性的元素且属性值为该值的元素对象
*/
@Test
public void parse04() throws Exception {
//a.创建解析器对象
SAXReader saxReader = new SAXReader();
//b.把XML加载成Document对象
Document document =
saxReader.read(XpathDemo.class.getResourceAsStream("/Contacts2.xml"));
//c.检索数据
List<Node> nodes = document.selectNodes("//@id");
for (Node node : nodes) {
Attribute attr = (Attribute) node;
System.out.println(attr.getName() + "====>" + attr.getValue());
}
//查询name元素(包含id属性的)
// Node node = document.selectSingleNode("//name[@id]");
Node node = document.selectSingleNode("//name[@id=888]");
Element ele = (Element) node;
System.out.println(ele.getTextTrim());
}
}
设计模式
工厂模式
public abstract class Computer {
private String name;
private double price;
public abstract void start();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
public class Mac extends Computer{
@Override
public void start() {
System.out.println(getName() + "优雅启动");
}
}
public class HuaWei extends Computer{
@Override
public void start() {
System.out.println(getName()+ "菊花");
}
}
public class FactoryPattern {
//定义一个方法,创建对象返回
public static Computer createComputer (String info){
switch (info){
case "mac" :
Computer c1 =new Mac();
c1.setName("MacBook pro");
c1.setPrice(19999);
return c1;
case "huawei" :
Computer c2 = new HuaWei();
c2.setName("HuaWei pro 16");
c2.setPrice(59999);
return c2;
default:
return null;
}
}
}
public class FactoryDemo {
public static void main(String[] args) {
Computer c1 = FactoryPattern.createComputer("huawei");
c1.start();
Computer c2 = FactoryPattern.createComputer("mac");
c2.start();
}
}
装饰模式
//父类
public abstract class InputStream {
public abstract int read();
public abstract int read(byte[]buffer);
}
//原始类
public class FileInputStream extends InputStream{
@Override
public int read() {
System.out.println("以低性能的方式读取一个字节a");
return 97;
}
@Override
public int read(byte[] buffer) {
buffer[0] = 97;
buffer[1] = 98;
buffer[2] = 99;
System.out.println("以低性能的方式读取了一个字节数组:" + Arrays.toString(buffer));
return 3;
}
}
//装饰类:继承InputStream 扩展原始类的功能
public class BufferedInputStream extends InputStream{
private InputStream is;
public BufferedInputStream(InputStream is){
this.is = is;
}
@Override
public int read() {
System.out.println("提供8KB的缓冲区,提高数据性能");
return is.read();
}
@Override
public int read(byte[] buffer) {
System.out.println("提供8KB的缓冲区,提高数据性能");
return is.read(buffer);
}
}
public class DecoratorDemo {
public static void main(String[] args) {
InputStream is = new BufferedInputStream(new FileInputStream());
System.out.println(is.read());
System.out.println(is.read(new byte[3]));
}
}