JAVA高级(二)

1. 正则表达式

  • 作用:

    • 用来校验数据格式是否合法
    • 在一段文本中查找满足需求的内容
  • 书写规则:image-20241119143551049

2. 集合框架(一)

  • 集合体系结构
    • Collection单列集合
    • Map双列集合(键值对)

image-20241121105320555

  • Collection的遍历方式:
    • 迭代器Iterator
    • 增强for循环:可以用来遍历数组或者集合(本质是迭代器的简化写法)
//迭代器iterator
Collection<String> c = new ArrayList<>();
c.add("alex");
Iterator<String> it = c.iterator();
while(it.hasNext()){
String ele = it.next();
}
//增强for循环
for(String ele : c){
System
}
//forEach方法结合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);
  • List集合:有序、可重复、有索引
    • ArrayList集合特点
      • 基于数组实现
      • 查询效率低
      • 删除效率低
      • 添加效率极低
    • LinkedList(双向链表)
      • 查询慢(需要从开始查)
      • 增删相对快
      • 双向列表对首位元素进行增删改查的速度极快。
      • 应用场景:可以用来设计栈
//ArrayList
List<String> list = new ArrayList<>();
list.add("121");
list.add("232");
list.add(1,"222");
list.remove(2);
list.get(1);
list.set(1,"zz");
//LinkedList
LinkedList<String> queue = newe LinkedList<>();
queue.addLast("1");
queue,addLast("2");
queue.removeFirst();
  • Set集合:无序、不重复、无索引
    • HashSet:无序、不重复、无索引
    • LinkedHashSet:有序、不重复、无索引
      • 原理:每个元素额外多了一个双链表机制记录前后元素的位置
    • TreeSet:排序、不重复、无索引、可排序(基于红黑树实现)

image-20241121141703741

  • 注意事项:集合的并发修改异常
    • iterator会带来并发修改异常
//并发修改异常
Iterator<String> it =list.iterator();
while(it.hasNext()){
String name = it.next();
if(name.contains("李")){
list.remove(name);//并发修改异常
it.remove()//删除迭代器当前遍历的数据
}

3. 集合框架(二)

  • 可变参数:
    • 一种特殊的形参,可以不传数据给他;可以传一个或者多个数据给他,也可以传一个数组;可以更灵活的接收数据。
//可变参数
public static void test(int...nums){};
//Collections(工具类)
List<String> names = new ArrayList<>();
Collections.addAll(names,"zz","zhw","www");
Collections.shuffle(names);//打乱元素的顺序
List<Integer> list = new ArrayList<>();
list.add(3);
list.add(4);
Collections.sort(list);//对list排序
//如果是针对如Student类,需要在类里implements Comparable,重写compareTo方法
Collections.sort(Students,( o1, o2) -> Double.compare(o1.getHeight(),o2.getHeight()));
  • Map系列集合

    • 特点:

      • Map集合被称为双列集合,一次要存一堆数据作为一个元素

      • 键值对集合,一一对应

      • Map中所有键是不允许重复的

      Map<String,Integer> map = new HashMap<>();
      map.put("手机",100);
      map.put("手表",200);
      Map<Integer, String> map1 = new TreeMap<>();
    • Map的常用方法:

      map.size();
      map.clear();//清空
      map.isEmpty();//是否为空
      int v1 = map.get("手表");//根据键得到值
      map.remove("手表");//删除键值对(返回值)
      map.containsKey("手机");//判断
      map.containsValue(2);//判断是否含有某个值
      Set<String> keys = map.keySet();//获取Map集合的全部键
      Collection<Integer> values = map.values();
      map1.putAll(map2);//所有值加入到map1中
    • Map的遍历

      • 键找值
      • 键值对
      • Lambda
      //键找值
      Map<String, Double> map = new HashMap<>();
      map.put("zz",188.5);
      map.put("xx",177.6);
      map.put("cc",177.9);
      Set<String> keys = map.keySet();
      System.out.println(keys);
      for(String key : keys){
      double value = map.get(key);
      System.out.println(key+" "+value);
      }
      //键值对
      Set<Map.Entry<String, Double> >entries = map.entrySet();//获取所有的键值对的集合
      for(Map.Entry<String,Double> entry : entries){
      String key = entry.getKey();
      double value = entry.getValue();
      System.out.println(key+" "+value);
      }
      //Lambda
      map.forEach((k,v) ->{
      System.out.println(key+" "+value);
      });
      Map<Student, String> map = new TreeMap<>(new Comparator<Student>(){
      @Override
      public int compare(Student o1,Student o2){
      return Double.compare(o2.getHeight(),o1.getHeight());
      }
      });
      System.out.println(map);

image-20241127105904352

  • Stream 流
    • 定义:
      • 可以用来操作集合或者数组的数据
      • 结合了Lambda的语法风格编程,代码更简洁,可读性更好
List<String> names = new ArrayList<>();
Collections.addAll(names,"zz","zs","zd","sd");
List<String> list2 = names.stream().filter(s -> s.startsWith("z")).filter(a->a.length() == 2).collect(Collectors.toList());
System.out.println(list2);
//常用方法、、Set集合
Set<String> set = new HashSet<>();
Collections.addAll(set,"zz","zs","zd","sd");
Stream<String> stream1 = set.stream();
stream1.filter(s -> s.contains("z")).forEach(s -> System.out.println(s));
//Map集合的Stream流
Map<String,String> map = new HashMap<>();
map.put("zz",188.5);
map.put("xx",177.6);
map.put("cc",177.9);
//分别取出键、值流
Set<String> keys = map.keySet();
Stream<String> ks = keys.stream();
Collection<Double> values = map.values();
Stream<Double> vs = values.stream();
//键值对成对取出
Set<Map.Entry<String, Double>> entries = map.entrySet();
Stream<Map.Entry<String, Double>> kvs = entries.stream();
kvs.filter(e ->e.getKey().contains("z")).forEach(e -> System.out.println(e.getKey()));
//Arrays类取出流
String[] names = {"zza","zz","as"};
Stream<String> s1 = Arrays.stream(names2);
Stream<String> s2 = Stream.of(names2);
  • Stream流常见的中间方法
List<Double> scores = new ArrayList<>();
Collections.addAll(scores,88.5,75.2,12.54,12.3,98.5,2.0);
scores.stream().filter(s -> s >=60).sorted().forEach(s -> System.out.println(s));
// Student 类
//年龄在23到30之间,年龄降序输出
List<Student> students = new ArrayList<>();
students.stream().filter(s -> s.getAge() >=23 && s.getAge() <= 30).sort((o1,o2) -> o2.getAge() - o1.getAge()).forEach(s -> System.out.println(s));
//取出升高最高的三个学生,输出(用到limit,表示限制多少个)
students.stream().sort((o1,o2) -> Double.compare(o2.getHeight(),o1.getHeight()).limit(3).forEach(s -> System.out.println(s));
//取身高倒数的两个学生,并输出(用到skip,表示跳过多少个)
students.stream().sorted((o1,o2)->Double.compare(o2.getHeight(),o1.getHeight())).skip(students.size()-2).forEach(s -> System.out.println(s));
//找到超过168的学生的名字,并去除重复,在输出(用到map映射,加工数据(一种数据变成另一个数据))(distinct(去重复))
students.stream().filter(s -> s.getHeight()>168).map(s ->s.getName()).distinct().forEach(s -> System.out.println(s));
//若希望内容一致为重复,需要重写equals()和hashcode()方法
//可以使用concat方法合并两个流
Stream<String> st1 = Stream.of("zz","xx");
Stream<String> st2 = Stream.of("zz1","xx1");
Stream<String> st3 = Stream.concat(st1,st2);
allSt.forEach(System.out::println);
  • Stream流的终结方法
    • 终结方法指的是调用完成后,不会返回新的Stream了,没法再继续使用流了
//1.forEach()方法
//2.count()方法,long类型
long size = students.stream().filter(s -> s.getHeight() > 168).count();
System.out.println(size);
//找出身高最高的学生对象
//3.max(),min()方法
students.stream().max((o1,o2) -> Double.compare(o1.getHeight(),o2.getHeight()));
//4.收集stream流;转回到集合/数组中
//流只能收集一次
//collect()收集到List集合中,toList()/toSet()转为List/Set
List<Student> students1 = students.stream().filter(s -> s.getHeight > 170.0).collect(Collectors.toList());
Set<Student> students2 = students.stream().filter(s -> s.getHeight > 170.0).collect(Collectors.toSet());
//找到身高超过170的学生对象,并把学生的名字和身高存入到一个Map集合中,toMap()
Map<String, Double> map = students.stream().filter(a -> a.getHeight()>170).distinct().collect(Collectors.toMap(a ->a.getName,a->a.getHeight()));
Object[] arr = students.stream().filter(a -> a.getHeight()>170).toArray();
Student[] arr = students.stream().filter(a -> a.getHeight()>170).toArray(len -> new Student[len]);

4. File、IO流

4.1 概述

File类的对象,用于代表当前操作系统的文件(文件、文件夹)。

File类只能对文件本身进行操作,不能读写文件里面存储的数据。

IO流:用于读写数据的(可以读写文件,或网络中的数据…)

File f1 = new File("D:/resource/ab.txt");
File f1 = new File("D:"+File.separator+"resource"+File.separator+"ab.txt");
System.out.println(f1.length());
File f2 = new File("D:/resource");//文件夹
File f3 = new File("D/resource//aaa.txt");//不存在
System.out.println(f3.exists());//false

4.2 File操作方法

  • 判断文件类型、获取文件信息
File f3 = new File("D/resource//aaa.txt");//不存在
System.out.println(f3.exists());//false
System.out.println(f3.isFile());//true
System.out.println(f3.isDirectory());//false
System.out.println(f3.getName());//aaa.txt
long time = f3.lastModified();//最后修改时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
System.out.println(sdf.format(time));
f3.getPath();//D/resource//aaa.txt
f3.getAbsolutePath();//D/resource//aaa.txt
  • 创建文件、删除文件
File f1 = new File("D/resource//aaa.txt");//不存在
boolean flag = f1.createNewFile();//需要抛出异常,若存在返回false
File f2 = new File("D:/resource/aaa/bbb");
System.out.println(f2.mkdir());//false,只能创建一级文件夹
System.out.println(f2.mkdirs());//true,可以创建多级文件夹
System.out.println(f1.delete());//true(删除文件、空文件,不能删除非空文件夹)
System.out.println(f1.delete());//true
  • 遍历文件夹
    • 注意事项:
      • 当主调是文件,或者路径不存在,返回null
      • 当主调是空文件夹。返回空数组
      • 当没有权限时,返回null
File f1 = new File("D/resource");//不存在
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());
}
  • 文件搜索
//需求:找到QQ.exe文件的位置
File root = new File("D:/");
public static void searchFile(File dir, String fileName){
//1.把非法情况拦截
if(dir == null || !dir.exists()!! dir.isFile()){
return;
}
//2. 获取一级文件对象
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);
}
}
}
}

4.3 字符集

  • 常见字符集
    • ASCII字符集:使用一个字节存储一个字符,首位为0。
    • GBK(汉字内码扩展规范):GBK中一个中文字符编码为两个字节的形式存储。规定:第一个字节的第一位必须为1。
    • Unicode字符集(统一码,国际组织制定,可以容纳世界上所有文字、符号的字符集)。
    • UTF-8:可变长编码方案。英文、数字占一个字节,汉字字符占3个字节。

4.4 IO流

  • 概述:I为输入流,负责把数据读到内存中,O为输出流,负责写数据出去。
  • 分类:字节输入/输出流、字符输入/输出流。

image-20241206163035825

  • FileInputStream(文件字节输入流)
//FileInputStream(文件字节输入流)
//以内存为基准,可以把磁盘文件中的数据以字节的形式读入到内存中去
InputStream is = new FileInputStream(new File("file-io-app\\src\\001.txt"));
InputStream is = new FileInputStream("file-io-app\\src\\001.txt");//简化写法
int b1 = is.read();
System.out.println((char)b1);//a
int b2 = is.read();
System.out.println((char)b2);//b
int b3 = is.read();
System.out.println((char)b3);//-1(数据读完了)
//这种方法资源消耗比较大,而且容易出现乱码
//流使用完之后必须释放,将流关闭
is.close();
  • FileInputStream(文件字节输入流)读取多个字节
//FileInputStream(文件字节输入流)读取多个字节
InputStream is = new FileInputStream(new File("file-io-app\\src\\001.txt"));
//每次读取多个字节到字节数组中,返回读取的字节数量
byte[] buffer = new byte[3];
int len = is.read(buffer);//每次读取多少字节
String rs = new String(buffer);
int len2 = is.read(buffer);
String rs2 = new String(buffer, 0 ,len2);
/*------------------------------*/
//改造
byte[] buffer = new byte[3];
int len;
while((len = new String(buffer,0,len)) !=-1){
String rs = new String(buffer,,0,len);
System.out.println(rs);
}
//这种方法也会导致乱码的情况(汉字)
//readAllBytes
byte[] buffer = is.readAllBytes();
System.out.print(new String(buffer));
//如果文件过大,创建的字节数组也会过大,可能会导致内存溢出
  • FileOutputStream(文件字节输出流)
//FileOutputStream(文件字节输出流)
//1.创建一个字节输出流管道与文件接通
OutputStream os = new FileOutputStream("file-io-app/src/002.txt");
//默认为清空原有数据
//可以用
OutputStream os = new FileOutputStream("file-io-app/src/002.txt", append = true);
os.write(97);//97就是一个字节,代表a
os.write("b");
byte[] bytes = "我爱你中国abc".getBytes();
os.write(bytes);
os.write(bytes,0,15);
os.write("\r\n".getBytes());
os.close();
  • 文件复制
//文件复制
InputStream is = new FileInputStream("D:/resource/meinv.png");
OutputStream os = new FileOutputStream("C:/data/meinv2.png");
byte[] buffer = new byte[1024];
int len ;
while((len = is.read(buffer))!=-1){
os.write(buffer,0,len);
}
os.close;
is.close;
  • 释放资源的方式
    • finally代码区的特点:无论try中的程序是正常执行或是出现异常,最后一定会执行finally区,除非JVM终止
    • try-with-resource
//释放资源的方式
//中间出错导致没有机会关闭流,导致资源浪费
try{
System.out.println(10/2);
return;
}catch(Exception e){
e.printStackTrace();
}finally{
System.out.println("执行");
}
try{
return a/b;
}catch(Exception e){
e.printStackTrace();
return -1;
}finally{
System.out.println("执行");
return 111;//不要这样写,否则一定会执行
}
  • try-catch-finally
InputStream is =null;
OutputStream os =null;
try {

System.out.println(10/0);
is = new FileInputStream("aaa.txt");
os = new FileOutputStream("bbb.txt");
System.out.println(10/0);
byte[] buffer = new byte[2024];
int len;
while((len = is.read(buffer))!=-1){
os.write(buffer,0,len);
}
System.out.println("完成");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
  • try-with-resource

    该资源使用完毕后,会自动调用close()方法,完成资源的释放

try(
InputStream is = new FileInputStream("aaa.txt");
OutputStream os = new FileOutputStream('bbb,txt');
) {
byte[] buffer = new byte[2024];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
System.out.println("完成");
}catch (Exception e){
e.printStackTrace();
}

4.5 字符流

  • 作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去。
try(Reader fr = new FileReader("aaa.txt");){
int c;
while((c=fr.read())!=-1){
System.out.print(c);
}
char[] buffer = new char[3];
int len;
while((len = fr.read(buffer))!=-1){
System.out.print(new String(buffer,0,len));
}
}catch (Exception e){
e.printStackTrace();
}
try(Writer fw = new FileWriter("111.txt");){
fw.write('a');
fw.write(97);
fw.write('一');
fw.write("我爱你中国abc",0,5);
char[] buffer = {'a','b','c','d'};
fw.write(buffer);
fw.write(buffer, 0, 2);
fw.write("\r\n");//实现换行
}catch(Exception e){
e.printStackTrace();
}
  • 字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效。
fw.flush();
fw.close();//刷新之后还可以用,不刷新的话用不了
  • 字节流适合做一切文件数据的拷贝;字节流不适合读取中文内容输出
  • 字符流适合做文本文件的(读写)

5. IO的各种流

5.1 IO流-缓冲流

image-20241208170020507

  • 对原始流进行包装,一提高原始流读写数据的性能
  • BufferedReader、BufferedWriter自带8K缓冲池,可以提高字符输出流写字符数据的性能。(最好不要用多态写)
InputStream is = new FileInputStream("aaa.txt");
InputStream bis = new BufferedInputSystem(is);
OutputStream os = new FileOutputStream('bbb,txt');
OnputStream bos = new BufferedOutputSystem(os);
//BufferedReader
Reader fr = new FileReader("aaa.txt");
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readline)!=null){
System.out.println(line);
}
//BufferedWriter
bw.newline();//自动换行

5.2 IO转换流

  • 解决问题:被读取的文本文件会出现乱码

  • 字符输入转换流:InputStreamReader

  • 字符输出转换流:OutputStreamWrite

  • 解决思路:先获取文件的原始字符流,再将其按真实的字符集编码转为字符输入流,这样就不会乱码了

//1.字符输入转换流
InputStream is = new FileInputStream("aaa.txt");
Reader isr = new InputStreamReader(is, "GBK");
BufferedReader br = new BufferedReader(isr);
String line;
while((line = br.readline)!=null){
System.out.println(line);
}
//2.字符输出转换流
OutputStream os = new FileOutputStream("aaa.txt");
Writer osw = new OutputStreamWriter(os,"GBK");
BufferedWriter bw = new BufferedWriter(osw);
bw.wirte("一二三四1234");

5.3 IO打印流

  • PrintStream、PrintWriter
  • 可以实现高效、方便的打印数据出去,能实现打印杀出去就是啥出去
PrintStream ps = new PrintStream("aaa.txt");
PrintWriter ps = new PrintWriter("aaa.txt");//不能追加
PrintWriter ps = new PrintWriter(new FileOutputStream("aaa.txt",true));
ps.println(97);
ps.println('a');
  • 打印流的一种应用:输出语句的重定向:把输出语句的打印位置改到某个文件中。
try(PrintStream ps = new PrintStream("aaa.txt");){
System.setOut(ps);
System.out.println("一二三四");
}catch(Exception e){
e.printStackTrace();
}

5.4 IO数据流

  • DataOutputStream(数据输出流):允许把数据和其类型一起写出去。
DataOutputStream dos = new DataOutputStream(new FileOutputStream("aaa.txt"));
dos.writeInt(97);
dos.writeDouble('a');
dos.writeUTF("一二三四");//记录内容和编码类型
  • DataInputStream(数据输入流)
DataInputStream dis = new DataOutputStream(new FileInputStream("aaa.txt"));
int i = dis.readInt();
double d = dis.readDouble();
boolean b = dis.readBoolean();
String rs = dis.readUTF();

5.5 IO序列化流

image-20241209160102499

  • 从文件中读出、取出java对象
//对象如果需要序列化,必须实现序列化接口implements Serializable
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("aaa.txt"));
User u = new User("admin","张三", "666888");
oos.writeObject(u);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("aaa.txt"));
User u = (User)ois.readObject();
//如果不想参与序列化
private transient String code;

5.6 IO框架

  • 框架:

    • 解决某个问题,编写的一套类、接口等,可以理解为一个半成品
    • 好处:在框架的基础上开发,可以得到优秀的软件架构,提高开发效率
    • 框架的形式:一般是把类、接口等编译为class形式,再压缩为一个.jar结尾的文件。
  • IO框架:封装了JAVA提供的文件、数据操作的代码,对外提供更简单的方式对文件操作,对数据读写等。

  • Commons-io

image-20241209160832552

image-20241209161453294

6 特殊文件与日志技术

**特殊文件:**属性文件(properties)、xml文件

**日志技术:**程序运行的信息记录到文件中。

6.1 特殊文件——Properties

  • 是一个Map集合,但是一般不会当集合用
  • 核心作用:Properties是用来代表属性文件的,通过Properties可以读写属性文件的诶内容。
//1.创建一个properties
Properties properties = new Properties();
System.out.println(properties);
properties.load(new FileReader("users.properties"));
System.out.println(properties);
System.out.println(properties.getProperty("赵敏"));//根据键取值
Set<String> keys = properties.stringPropertyNames();
for(String key:keys){
String value = properties.getProperty(key);
System.out.println(key + "---->" + value);
}
porperties.forEach((k,v)-->{System.out.println(key + "---->" + value)});
Properties properties = new Properties();
properties.setProperty("a","aaa");
properties.store(new FileWriter("users.properties"), "i save");

6.2 特殊文件——XML文件

  • XML:可扩展标记语言
  • XML中写”>“、”&“等会出现冲突导致报错,可用以下字符替代
&lt; < 小于
&gt; > 大于
&amp; & 和号
&apos; ' 单引号
&quoot; " 引号
  • XML可以写一个CDATA的数据区:,里面的内容可以随便写

  • 解析XML数据:Dom4j(官网下载)

    • new - lib - Add as Library
SAXReader saxReader = new SAXReader();
Document document = saxReader.read("aaa.xml");
Element root = document.getRootElement();
System.out.println(root.getName());//根元素名字
//解析xml的具体数据
List<Element> elements = root.elements();
for(Element elememt :elements){
System.out.println(element.getName());
}
  • 利用StringBuilder添加XML格式的数据
StringBuilder sb = new StringBuilder();
sb.append("<?xml version = \"1.0\" encoding=\"UTF-8\" ?>\r\n");
sb.append("<book>\r\n");
try(
BufferedWriter bw = new BufferedWriter(new FileWriter("aaa.xml"));
){
bw.write(sb.toString());
} catch(Exception e){
e.printStackTrace();
}

6.3 日志技术

  • 日志技术体系:

    • 框架:JUL、log4j、Logback
    • 日志接口:JCL、SLF4J
    • logback是基于slf4j接口实现的
  • logback日志框架有以下几个模块

    • logback-core
    • logback-classic
    • logback-access
  • 实现步骤

    1. 导入Logback框架到项目中

    2. 将核心配置文件logback.xml放在src项目下面

    3. 创建Logger对象,调用其方法即可记录系统日志信息

public static final Logger LOGGER = LoggerFactory.getLogger("LogbackTest");
public static void main(String[] args){
try{
LOGGER.info("chu方法执行");
chu(10,0);
LOGGER.info("chu方法执行成功");
}catch(Exception e){
LOGGER.error("chu出现Bug");
}
}
public static void chu(int a, int b){
LOGGER.debug("a"+a);
LOGGER.debug("b"+b);
int c == a/b;
LOGGER.info("结果是" + c);
}
  • 日志级别
    • trace-debug-info-warn-error

7. 多线程

7.1 概述

  • 线程是一个程序内部的一个执行流程

  • 多线程是从软硬件实现多条执行线程的技术

  • 多线程的创建

    • 方式一:继承Thread类(extends)
      • 缺点:只能继承,单继承
    • 方式二:实现Runnable接口(implements)
      • 优点:只是实现接口,可以继承其他类、实现其他接口,扩展性强
    • 方式三:实现Callable接口(implements)
      • 实现Runnable对象
      • 可以在线程结束之后,用未来任务对象调用get方法获取线程执行完毕的结果
      • 优点:扩展性强;可以拿到结果
      • 缺点:编码复杂
Callable<String> call = new MyCallable(100);
FutureTask<String> f1 = new FutureTask<>(call);
new Thread(f1).start();
String rs = f1.get();
System.out.println(rs);

7.2 Thread常用方法

t.sleep(5000);//让t线程休眠5s

t.join();//让t线程先执行完

7.3 线程安全与线程同步

  • **线程安全:**多个线程,同时操作同一个共享资源的时候,可能出现业务安全的情况。

  • 线程同步:

    • 让多个线程先后依次地访问共享资源。
  • 方式一:同步代码块

    • 作用:把访问共享资源的核心代码上锁
    • 方式:CTRL+ALT+T+synchronized
    • 用this作为锁:不会干扰他人
    • 如果是静态方法,可以使用当前类名.class(Account.class)
  • 方式二:同步方法

    • 把访问的核心方法上锁
    • public synchronized void drawMoney(double money)
  • 方式三:Lock锁

    • 可以自己创建锁对象,更灵活、方便
    • Lock是接口,不能直接实例化,可以采用实现类ReentrantLock来创建锁对象。

7.4 线程池

  • 概念:线程池是一个可以复用线程的技术
  • 问题:创建新线程的开销很大
  • 结构:工作流程、任务队列
  • JAVA提供线程池的接口:ExecutorService

image-20241220201621466

ExecutorService pool = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,
new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
  • 注意:

    • 临时线程什么时间创建:新任务提交发现核心线程在忙,任务队列也满,并且还可以创建临时线程,此时会创建临时线程。
    • 核心线程和临时线程都在忙。任务队列也满,开始拒绝任务。
  • 核心线程数应该配置多少

    • 计算密集型任务:核心线程数量 = CPU的核数+1;
    • IO密集型任务:核心线程数量 = CPU的核数*2;
    • 高并发场景中使用Executors如果不注意可能会出现风险。最好使用ThreadPoolExecutor

7.5 并发、并行

  • 进程:

    • 进程:正在运行的程序(软件)就是一个独立的进程。
    • 线程属于进程,一个进程可以同时运行多个线程。
    • 进程中的多个线程其实是并发和并行的。
  • 线程的生命周期

    • New
    • Runnable
    • Terminated
    • Blocked
    • Waiting
    • Timed Waiting

8. 网络编程

8.1 网络编程概述

  • java.net.包下提供了网络编程的解决方案

  • 通信结构:(都依赖于网络编程)

    • CS架构:Client客户端/Server服务端
      • 客户端需要程序员开发、用户安装
      • 服务端需要程序员开发
    • BS架构:Browser浏览器/Server服务端
      • 客户端不需要程序员开发实现
      • 服务端需要程序员开发实现

8.2 网络通信三要素

  • 要素
    • IP:设备在网络中的地址,是唯一标识
    • 端口:应用程序在设备中唯一的标识
    • 协议:连接和数据在网络中传输的规则

8.2.1 IP地址

  • ip地址

    • IP:全称互联网协议地址,是分配上网设备的唯一地址
    • IP地址两种形式:IPV4、IPv6
      • IPv4:32位
      • IPv6:128位
  • IP域名

    • 域名→dns服务器→ip→访问服务器→返回数据在浏览器上
  • 公网、内网IP

    • 公网IP:是可以连接互联网的IP的地址
    • 内网IP:也叫局域网IP,只能组织机构内部使用
    • 192.168开头的就是内网IP
  • 常用命令:

    • ipconfig:查看本机IP
    • ping IP地址:检查网络是否联通
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));

8.2.2 端口号

  • 端口:标记在计算机设备上运行的应用程序,被规定为16位的二进制(0~65535)。
  • 分类:
    • 周知端口(0~1023):预先被某些知名应用占用
    • 注册端口(1024~49151):分配给用户进程或某些应用程序
    • 动态端口(49152~65535):动态分配
    • 我们自己开发的程序一般选择使用注册端口,一个设备不能出现两个程序的端口号一样

8.2.3 协议

  • 协议:网络上的通信设备,事先规定的连接规则,以及传输数据的规则被称为网络通信协议
  • 开放式网络互联标准:OSI网络参考模型
    • OSI网络参考模型:全球网络互联标准
    • TCP/IP协议:事实上的国际标准
    • 程序员主要考虑应用层

image-20241222201249511

  • UDP:用户数据报协议
    • 特点:无连接、不可靠通信
    • 不事先建立连接,一般数据包含:自己的IP、程序端口、目的地IP、和数据(64KB内)
    • 发送方不管对方在不在线,数据在中间丢失也不管,如果接收方收到数据也不返回确认,不可靠
  • TCP:传输控制协议
    • 特点:面向连接、可靠通信
    • 最终目的:保证在不可靠的信道上实现可靠传输
    • 主要三个步骤实现:三次我手建立连接,传输数据进行确认、四次挥手断开连接
    • 三次握手:
      • 可靠链接:确定通信双方,收发消息都是没有问题的
    • 四次挥手:
      • 目的:确保双方数据收发都已经完成

8.3 UDP通信

  • Java提供了一个java.net.DatagramSocket类实现UDP通信

  • 客户端

DatagramSocket socket = new DatagramSocket(7777);
byte[] bytes = "xxxx".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(bytes,bytes.length
, InetAddress.getLocalHost(),6666);
socket.send(packet);
System.out.println("done");
socket.close();//释放资源
  • 服务端
DatagramSocket socket = new DatagramSocket(6666);
//2.创建一个数据包对象,用于接收数据
byte[] buffer = new byte[1024*64];
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
//3.使用数据包接收数据
socket.receive(packet);
int len = packet.getLength();
String rs = new String(buffer,0,len);
System.out.println(rs);
System.out.println(packet.getAddress().getHostAddress());
System.out.println(packet.getPort());
socket.close();

8.4 TCP通信

  • 特点:面向连接、可靠通信

  • JAVA提供了一个java.net.Socket类实现TCP通信

  • 客户端

Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
Scanner sc= new Scanner(System.in);
while (true) {
System.out.println("say it");
String msg = sc.nextLine();
if("exit".equals(msg)) {
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush();
  • 服务端
//1.创建ServerSocket对象,同时为服务器设计端口
ServerSocket serversocket = new ServerSocket(8888);
Socket soc = serversocket.accept();
//3.从socket通信管道中得到一个字节输入流
InputStream is = soc.getInputStream();
//4.把原始的字节输入流包装为数据输入流
DataInputStream dis = new DataInputStream(is);
while (true) {
try {
String rs = dis.readUTF();
System.out.println(rs);
} catch (Exception e) {
System.out.println(soc.getRemoteSocketAddress()+"离线了");
dis.close();
soc.close();
break;

9. JAVA高级

9.1 单元测试

指对最小的功能单元(方法),编写测试代码对其进行正确性测试

9.1.1 Junit单元测试框架

  • 可以用来对方法进行测试,很多开发工具已经集成了Junit框架,如IDEA
  • 优点:可以灵活的编写测试代码,自动生成测试报告

image-20241230160249700

9.2 反射

反射就是加载类,允许以编程的方式解析类中的各种成分(成员变量,方法、构造器)

  • 第一步:加载类,获取类的字节码。