目录

Java

概述

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

概念

  • javac编译,java执行,javap xxx.class反编译,jre:核心类库 + jvm
  • 大小写敏感

工具

IDEA

command + alt + ←:回退

command + D:复制当前行到后一行

结构:项目 - 模块 - 包 - 类

基本语法

注释

/**
* meaning
* @param arg1 mean
*/

&& 与 & 的区别:左侧为 false 右侧不再执行

变量

  • 成员变量:类中、方法体外,创建对象时实例化,可被类中方法访问
  • 类变量:类中、方法体外,必须为static类型
  • 局部变量:栈中分配,声明时必须初始化,不可用访问修饰符

static

静态,修饰成员变量和成员方法

static修饰,静态变量:类变量,只存一份,被类的全部对象共享,通过类访问,classname.var

无static修饰,实例变量:对象的变量,属于每个对象

图片无法加载

static 修饰,静态方法:属于类, classname.func,设计工具类,可private私有化构造函数

无static修饰,实例方法,属于对象

  • 静态方法可直接访问静态成员,不可直接访问实例成员
  • 实例方法既可访问静态成员也可访问实例成员
  • 实例方法可出现this,静态方法不可出现this关键字

数据结构

数组

double[] xxx = new double[8]; // 动态

String[] name = new String[]{"aa", "bb"};
String[] name = {"a", "b"};
String name[] = {"a", "b"};

name.length; // 长度
Arrays.toString(xxx); // 打印数组

二维数组

// 动态
int[][] arr = new int[][]{ {x,x}, {x, x}};
int[][] arr = new int[2][2]; // 2 可换为变量

//静态
String[][] s = {
	{"a", "b"},
	{"c", "d"},
};

字符串

  • String,对象类型
String s = "aaa";

String ss = new String("abc");

char[] chars = {'a', 'b'};
String sss = new String(chars);

byte[] bytes = {97, 98};
String sss = new String(bytes);

区别

  • ""方式的字符串对象,存储在字符串常量池,相同内容只存一份

  • new方式的字符串对象,new一次产生一个新对象在堆内存中

方法

s.length(); // 返回大小
s.equals("xxx"); // 判断是否相等
s.substring(0, 2); // 返回[0, 2)子串
s.charAt(index); // 返回索引对应的字符
s.startsWith("xx"); // 以xx开始

字符串拼接+号性能差,String为不可变对象,+后一直生成新对象

// 更高效的字符串拼接
StringBuilder sb = new StringBuilder(); // 可变内容容器
sb.append("xx").append('a'); // 支持链式编程
String s = sb.toString();

集合

容器,大小可变,泛型类

Collection

单列集合

接口:Collection<E>

实现类:

  • List<E>:有序、可重复、有索引
    • ArrayList<E>
    • LinkedList<E>
  • Set<E>:无序、不重复、无索引
    • HashSet<E>:无序、不重复、无索引
      • LinkedHashSet<E>:有序、不重复、无索引
    • TreeSet<E>:大小默认升序排序、不重复、无索引

Collection方法

c.add("xx");
c.size();
c.remove("aa");
c.isEmpty(); // 是否为空
c.clear(); // 清空集合
c.contains(obj); // 是否存在某个数据

Object[] arr = c.toArray(); // 集合转换成数组
String[] arr2 = c.toArray(String[]::new); // 集合转换成字符数组

遍历方式

迭代器 Iterator 遍历集合

Iterator<E> iterator() // 返回集中中迭代器对象,默认指向当前集合第一个元素
Iterator<String> it = xx.iterator();

// 方法
hasNext() // 当前位置是否有元素存在
next() // 获取当前位置元素,同时迭代器对象指向下一个元素处
it.remove(xxx); // 防止并发修改异常

遍历

// 迭代器遍历
while(it.hasNext()){
	String e = it.next();
}

// 下列遍历无法解决并发修改异常问题
// 增强 for 循环 java5引入
for(String name : names) {
	xxx
}

// Lambda 表达式
names.forEach(new Consumer<String>() {
	@Override
	public void accept(String s) {
		System.out.println(s);
	}
};

// 简化
names.forEach(s -> System.out.println(s));
// 简化
names.forEach(System.out::println);

并发修改异常:在遍历同时使用增删查改

List
add(index, "xx")
add("xxx")
remove // 返回删除的数据
get
set    // 返回修改前的数据

ArrayList:数组,查询速度快,增删数据效率低

  • 第一次添加数据开始扩容,扩成10,后面再次扩容为1.5倍
ArrayList<String> list = new ArrayList<>();

list.add("xxx");// 添加数据
list.get(index); // 取数据
list.size(); // 大小
list.remove(index); // 索引删除
list.remove("xx"); // 直接删除
list.set(index, "xxx"); // 修改
list.subList(start_index, end_index); // 截取

LinkedList :双向链表,有前后结点地址,有头节点和尾节点,查询慢,增删相对快,可实现对列和栈

addFirst() // 列表开头插入指定元素
addLast() // 元素追加列表末尾
getFirst() // 第一个元素
getLast() // 最后一个元素
removeFirst() // 删除并返回第一个元素
removeLast() // 删除并返回最后一个元素

// 栈
push(); 
pop();
Set

HashSet

  • 每个对象都有一个哈希值(int类型随机值),hashCode() ,哈希表实现:数组+链表+红黑树
  • 默认16长度数组,名为table,默认加载因子0.75,超过16*0.75时扩容,同位置用链表连接,新元素在旧元素后
  • 链表长度超过8,数组长度≥64时,自动将链表转为红黑树
Set<String> set = new HashSet<>();

set.add("xx");

HashSet无法对对象去重,new的对象地址不同,

实现自动去重,需重写对象的hashCode()equals()方法

@Override
public int hashCode() {
    return Objects.hash(field1, field2, ...); // field为对象字段
}

@Override
public boolean equals(Object o) {
    if (this == 0) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Classname cn = (Classname) o;
    return field1 == cn.field1 && Objects.equals(field2, cn.field2);
    // 分别为 int 比较和 String 比较

LinkedHashSet

哈希表(数组、链表、红黑树),每个元素额外多一个双链表机制记录前后元素位置

TreeSet

基于红黑树排序,对象需自定义排序规则

// 对象类实现一个Comparable比较接口,重写compareTo方法,指定大小比较规则
implements Comparable<Classname> {
    @Override
    public int compareTo(Classname o) { // 规定: >+, <-, =0
        // t2.compareTo(t1)
        // t2 == this
        // t1 == o
        return this.getAttr() - o.getAttr(); // 升序
    }
}

// public TreeSet (Comparator c) 集合自带比较器Comparator对象指定比较规则

Collections工具类

操作集合的工具类

List<String> list = new ArrayList<>();
Collections.addAll(list, "xx", "bb"); // 可变参数
Collections.shuffle(list); // 打乱顺序
Collections.sort(List<T> list, Comparator<? super T> c) // 排序

Map

双列集合

  • 键值对集合,键不可重复,值可重复
  • Map<K, V>,键值对均可为null
Map<String, Integer> map = new HashMap<>();
// 常用方法
map.put("a", 1); // 加入内容,返回被覆盖数据
map.get("a"); // 根据键取值
map.containsKey(key);
map.containsValue(value);
map.remove(key);
map.clear();
map.isEmpty();
// 获取所有键集合
Set<String> keys = map.keySet();
// 获取所有值集合
Collection<Integer> values = map.values();

遍历

Set<String> keys = map.keySet();
for(String key : keys) {
    Integer value = map.get(key);
}

// 利用 Set<Map.Entry<K, V>> entrySet()
Set<Map.Entry<String, Double>> entries = map.entrySet();
for(Map.Entry<String, Double> entry : entries) {
    String key = entry.getKey();
    Double value = entry.getValue();;
}

map.forEach((k, v) -> System.out.println(k + "=" + v);

堆中存对象和类地址,类及类方法在方法区中

this用于在方法中拿到当前对象,解决变量名称冲突

public class Object {
	private type name;
	
	// public 修饰的 get 和 set 方法
	public void setName(xx){};
	
	// 构造函数 创建对象会调用
	public Object(xxx){ // 可有参数, 有参存在后无参需要自己构造
		name = xxx;
	}
	
	public type func(){
		xxx
	}
}

Object ob = new Object();
ob.name = "xxx";
ob.func();

继承

public class Son extends Dad {}
  • 子类能继承父类非私有成员,优先访问自己类,super.xxx来指定访问父类的变量和方法
  • 所有类均为Object类的子类

权限修饰符

  • private:只能本类访问,类和接口不能声明为private,不能被子类继承
  • 缺省:本类、同一个包中的类可访问
  • protected:本类、同一个包中的类、子孙类中可访问

方法重写

  • 方法名称、参数列表一样,覆盖
  • 子类重写父类方法时,访问权限必须大于或等于父类方法权限
  • 私有方法、静态方法不能被重写
@Override
public void xx() {}

重写Object类的toString方法

  • 子类构造器先调用父类构造器,再执行自己
  • 默认情况下,子类全部构造器第一行代码都是 super(),会调用父类的无参数构造器
  • 若父类没有无参构造器需要在子类构造器第一行手写 super(…),指定调用父类的有参构造器

this 调用兄弟构造器:首行

this(xxx, xxx, xxx, "xxx");

多态

成员函数运行子类的,成员变量看父类的

  • 父类引用子类对象
  • 父类类型变量作为参数,可接收子类对象
  • 多态无法调用子类独有功能 → 判断后强转
Animal d = new Dog();
Animal w = new Wolf(); // 子类

类型转换

  • 自动类型转换:Person p = new Teacher();

  • 强制类型转换:Teacher t = (Teacher) p;

    • 判断真实类型后再强转:
    a1 instanceof Student

lombok自动添加构造函数和get,set函数

import lombok.Data;

@Data
@AllArgsConstructor // 有参构造
@NoArgsConstructor  // 无参构造

final 修饰类、方法、变量

  • 最终类:不能被继承
  • 最终方法:不能被重写
  • 该变量有且仅能被赋值一次,final + static 系统配置变量
  • 修饰引用类型的变量,变量存储的地址不能被改变,所指向对象内容可变

代码编译后,常量被宏替换,替换为字面量

实体类

  • javabean,需提供无参数构造器

  • 类中成员变量全私有,提供 public 修饰的 getter/setter方法

  • 实体类对象只负责数据存取,对数据的业务处理交给其他类的对象完成(XxxOperator

单例类

该类只能创建一个对象

// 饿汉式
public class A {
	// 定义一个静态变量,用于基本本类的一个唯一对象
	private static A a = new A();
	
	// 私有化构造器
	private A() {
	}
	
	// 提供公开静态方法,返回该类唯一对象
	public static A getInstance() {
		return a;
	}
}

// 懒汉式 用对象时才创建
public class B {
	// 定义一个类变量存储对象
	private static B b;
	
	private B() {
	}
	
	// 提供一个类方法返回类的一个对象
	public static B getObject() {
		if (b == null){
			b = new B();
		}
		return b;
	}
}

枚举类

  • 最终类,不可被继承,构造器私有
public enum A {
    X, Y, Z; // 只能写枚举类的对象名称,且用逗号隔开
    // 均为常量,每个对应一个对象
    ...
}

A a1 = A.X;
// a1.name() 名字
// a1.ordinal() 索引

抽象类

可修饰类和成员方法

public abstract class A{
    public substract void func(); // 只能有方法签名,无方法体
}
  • 抽象类中不一定有抽象方法,有抽象方法一定是抽象类
  • 抽象类不能创建对象,仅作为父类被子类继承并实现,重写所有抽象方法,支持多态

抽象类:建议使用 final 修饰模板方法,父类定义抽象方法,子类实现抽象方法

内部类

定义在类的内部

1️⃣ 成员内部类

  • 可以访问外部类静态成员,静态方法,实例成员
  • 可以获取当前寄生的外部类对象:外部类名.this
// 无 static 修饰,属于外部类对象持有
public class Outer {
	public class Inner {
		xx
	}
}

Outer.Inter oi = new Outer().new Inner();

2️⃣ 静态内部类

有 static 修饰,属于外部类自己持有,可访问外部类的静态成员

public class Outer {
	public static class Inner {
	}
}

Outer.Inner in = new Outer.Inner();

3️⃣ 匿名内部类

本质是一个子类,立即创建一个子类对象

new Animal(args...){
	@Override
	public void cry() {
	}
};

匿名类实现排序

Arrays.sort(T[] a, Comparator<T> c);

new Comparator<T>() {
	@Override
	public int compare(T o1, T o2) {
		// +: o1 > o2, -: o1 < o2, 0: o1 = o2
		...
	}
}

简化:函数式编程,Lambda只能简化函数式接口的匿名内部类

@FunctionalInterface // 声明函数式接口:只有一个抽象方法的接口
interface Swim {
	void swimming();
}

// lambda 重写swimming了, 进一步呢简化,参数类型也可省略,只有一个参数,()也可省略,一行代码则省略;和return
Swim s = (args) -> {
	xxxx
};
s.swimming(args);

简化 Lambda:方法引用

静态方法引用:类名::静态方法,Lambda表达式只调用一个静态方法, →前后参数形式一致

 Arrasy.sort(Students, (o1, o2) -> o1.getAge() - o2.getAge());
 
 // compareByAge(o1, o2)为静态方法
 Arrays.sort(Students, Student::compareByAge);

实例方法引用:对象名::实例方法 ,Lambda表达式只通过对象名称调用实例方法,→前后参数一致

Student t = new Student();
Arrays.sort(Students, (o1, o2) -> t.compareH(o1, o2));

// 简化
Arrays.sort(Students, t::compareH);

特定类型方法引用:

Arrays.sort(names, (o1, o2) -> o1.comparexxx(o2));
// 简化,o1和o2为特定类型String
Arrays.sort(names, String::comparexxx);

构造器引用:类名::new

包装类

byte-Byte, short-Short, int-Integer, long-Long, char-Character, float-Float, double-Double, boolean-Boolean

// 手工包装
Integer it = Integer.valueOf(100); // 130超过127边界,会new新的对象

// 自动装箱 
Integer it = 100;

// 自动拆箱
int i = it;

功能

基本类型转字符串

int j = 23;
String rs = Integer.toString(j);

// 转换为字符串
String a = j + ""; // 也可

字符串数值转换为对应基本数据类型

String str = "91";
int i = Integer.parseInt(str);
int i = Integer.valueOf(str); // 均可

基本操作

随机数

Random r = new Random();
r.nextInt(100) + 1; // 1-100

Math.random() // 返回[0, 1)的随机小数

代码块

静态代码块:static{},类加载时优先自动执行,只会执行一次,可完成类及静态资源的初始化

实例代码块:{},创建对象时执行,在构造器前执行,对实例变量初始化

输入输出

Scanner sc = new Scanner(System.in);
if(sc.hasNext()) {
	String xx = sc.next();
}
sc.close()

时间

long start = System.currentTimeMillis(); // 此刻时间毫秒值

LocalDateTime now = new LocalDateTime.now(); // 可直接输出
now.getYear();
now.getDayOfYear();
// 格式化
DataTimeFormatter dtf = DataTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss EEE a");
String res = dtf.format(now);

字符集

// 编码
String name = "fewio";
byte[] bytes = name.getBytes(); // UTF-8编码
Arrays.toString(bytes);

// 解码
String name2 = new String(bytes, "GBK");

高级操作

异常

Java.lang.Throwable
- Error: 系统级别错误
- Exception: 异常
	- RuntimeException 运行时异常编译不出错运行时出错
	- other
	编译时异常编译报错

处理异常

public static void xx() throws ParseException { // 所有异常,直接throws Exception
	xxx
	throw new ParseException();
}

try {
	xxx
} catch (ParseException e) {
	// throw new RuntimeException(e);
	e.printStackTrace(); // 打印异常信息
}finally{
  xxx
}

自定义异常

public class XException extends Exception { // RuntimeException 运行时异常   
	// 无参有参构造器
	public XException(String message){
		super(message);
	}
}

if(xx){
	throw new XException("wrong info");
}

接口

  • 接口不能创建对象
  • 无构造函数,方法只能被类实现,extends继承接口
  • 类要实现接口中所有方法,否则声明为抽象类
// JDK 8 以前
public interface interfacename {
    // 可省略 public final static 公开
    // 成员变量(常量)
    // 成员方法(抽象方法) void func();
}

// JDK 8 以后
public interface A {
    // 默认方法,默认加上public,只能使用接口的实现类对象调用
    default void test1(){
        ...
    }

    // 私有方法,JDK 9 以后, 使用接口中其他实例方法来调用
    private void test2() {
        ...
    }

    // 静态方法,默认加上public,只能使用当前接口名调用
    static void test3() {
        ...
    }
}
  • 接口是用来被类实现的,实现接口的类为实现类,一个类可同时实现多个接口,在其中重写抽象方法
public class impcla implements interf1, interf2, interf3 {
interfacename xx = new impcla();
xx.func();
  • 一个接口可以同时继承多个接口,需要被继承接口函数签名不冲突
  • 一个类继承父类,同时实现接口,父类中和接口有同名方法,优先使用父类方法,使用接口需要:子类自定义新函数中使用接口名.super.func()
  • 一个类实现多个接口,存在同名默认方法,可以重写方法防止冲突

泛型

泛型类、泛型接口、泛型方法

  • 提供了在编译阶段约束所能操作的数据类型,自动检查
ArrayList<String> list = new ArrayList<String>();

public class ArrayList<E> {
	...
}

泛型类

public class ArrayList<E> { // 大写:ETKV 
	void func(E e){
		xxx;
	}
}

泛型接口

public interface A<E>{
	
}

public class X implements A<E> {
}

泛型方法

public static <T> void test(T t) {
	//定义方法时同时自定义声明的T
}

通配符:? 使用泛型时代表一切类型,上下限

泛型上限:? extends C: 能接收的必须是C或其子类

泛型下限:? super C: 能接收的必须是C或其父类

public static void go(ArrayList<? extends XXX> xx) {
}
  • 泛型不支持基本数据类型,只支持对象/引用数据类型,Object类t,包装类解决

Stream流

  • java.util.stream.*,操作集合或数组
List<String> list = new ArrayList<>();
// 过滤并收集
List<String> newlist = list.stream().filter(s -> s.startsWith("xx")).filter(s -> s.length() == 3).collect(Collectors.toList());

获取集合的Stream流

Collection<String> list = new ArrayList<>();
Stream<String> s = list.stream();

获取数组的Stream流

String[] names = {"a", "b"};
Stream<String> s = Arrays.stream(names);

Stream<String> s1 = Stream.of(names);

流方法,中间方法调用完方法后会返回新的流

s.filter() // 过滤
.sorted() // 排序
.sorted((s1, s2) -> Double.compare(s2. s1)) // 重写规则排序
.limit(2) // 只要前2个
.skip(2) // 跳过前2个
.distinct() // 去重
.map(s -> "加工后: " + (s + 10)).forEach(System.out::println); // 映射为新数据放入流上
.concat(stream1, stream2) // 合并两个流为一个流

流获取结果

.forEach(System.out::println) // 遍历输出
.count() // 统计流中的个数

Otional<Teacher> max = teachers.stream().max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary()));
Teacher maxTeacher = max.get(); // 获取薪水最高的老师
Teacher minTeacher = min.get();

收集:结果转回集合或数组中,流只能收集一次

List<String> list1 = stream1.collect(Collectors.toList());
Set<String> set1 = stream1.collect(Collectors.toSet());
Object[] array = stream1.toArray();

Map<String, Double> map = stream1.collect(Collectors.toMap(Teacher::getName, Teacher::getSalary));

数组到集合中:

Set<String> set2 = newHashSet<>();
set2.addAll(list1);

函数式接口

  • 创建一个匿名内部类,实现该接口
  • 大括号中需要重写apply方法
new Function<Type1, Type2>() { xxx }
// 该接口接受一个类型为Type1的输入参数返回一个类型为Type2的结果

可变参数:形参中只能有一个,且放在形参列表最后

public static void x(int...nums){
    nums.length;
}

IO流

实际对文件中数据进行读写,不适合处理中文字符

字节流

FileInputStream,文件字节输入流,磁盘文件数据到内存中

InputStream is = FileInputStream("xx"); // 参数也可为 File 对象

// int b;
// while((b = is.read()) != -1) { // 每次读取一个字节
// 	System.out.println((char) b);
// }

// 无法避免中文截断导致乱码
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1) {
	String str = new String(buffer); // 字节数组转换为字符串输出
}

// 一次读完文件中所有字节, 适用于小文件
byte[] bytes = is.readAllBytes();
String rs = new String(bytes);

FileOutputStream,内存写到文件中

OutputStream os = FileOutputStream("xx", true); // true表示追加数据, 不覆盖
os.write('s');
os.write(97);

// 写入一个字节数组
byte[] bytes = "foewifjow\\r\\n".getBytes();
os.write(bytes);

// 写入一个字节数组一部分
os.write(bytes, 0, 3); // 偏移0处,长度为3

os.close(); // 关闭流

资源释放

try {
	...
} catch (IOException e) {
	e.printStackTrace();
} finally {
	...
	// 无论 try 中程序是否正常执行,最终均会执行finally区,除非JVM终止
}

try-with-resource

// try-with-resource 更实用
try(// 只能放置资源对象,用完会自动调用close函数关闭
	InputStream fis = new FileInputStream(srcPath);
	OutputStream fos = new FileOutputStream(destPath);
) {
		xxxx
} catch (Exception e {
		e.printStackTrace();
}

资源一般指最终实现了AutoCloseable接口

图片无法加载

字符流

适合中文字符

FileReader,文件字符输入流,文件数据以字符形式读入到内存

// 返回读取多少字符
Reader fr = new FileReader("xx");

char[] chs = new char[3];
int len;
while((len = fr.read(chs)) != -1){
	String str = new String(chs, 0, len); // 0:offset
}

FileWriter,文件字符输出流,内存数据字符形式写出文件中

  • 写出数据后,必须刷新流或关闭流,数据才生效
Writer fw = new FileWriter("xx", true); // true 追加

fw.write('a'); // 写一个字符
fw.write("aa"); // 写一个字符串
fw.write("java".toCharArray()); // 写一个字符数组
fw.write("few", 1, 2); // offset:1, len:2

fw.flush(); // 数据全部写出去

缓冲流

  • 为提高原始字节字符流性能
  • BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter
  • 缓冲字节输入流、缓冲字节输出流、缓冲字符输入流、缓冲字符输出流

缓冲流自带8KB缓冲池

// 把低级字节流包装成高级流
InputStream fis = new FileInputStream(path);
InputStream bis = new BufferedInputStream(fis);

// 把低级字符流包装成高级流,缓冲字符输入流提供新方法 readLine()
Reader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr);

String line;
while((line = br.readLine()) != null) {
	System.out.println(line);
}

// 缓冲字符输出流提供新方法换行 newLine()
bw.newLine();

字符输入转换流

  • 继承自字符输入流,解决不同编码乱码问题
InputStream is = new FIleInputStream("xx");
Reader isr = new InputStreamReader(is, "GBK");

打印流

继承自字节输出流和字符输出流,实现打印啥就是啥

PrintStream ps = new PrintStream("aa");
ps.println(97);
ps.println('a');
ps.println(true); // 打印true

数据流

DataInputStream,DataOutputStream,允许把数据和其他类型一并写出去

DataOutputStream dos = new DataOutputStream(new FileOutputStream("a"));

dos.writeByte(34);
dos.writeUTF("你好");
dos.writeInt(12323);
dos.writeDouble(9.9);

DataInputStream dis = new DataInputStream(new FileInputStream("aa"));
dis.readByte();
dis.readUTF(); //...

File

  • File: java.io包下的类,操作文件本身
  • 相对路径从工程下项目文件找
File f = new File("path");
f.length(); // 字节个数
f.getName(); // 文件名字
f.isFile(); // 是否是文件
f.isDirectory(); // 是否是文件夹

f.exists(); // 判断是否存在
f.createNewFile(); // 创建文件
f.mkdir(); // 创建文件夹,只创建一级
f.mkdirs(); // 创建多级文件夹
f.delete(); // 删除文件及空文件夹,返回是否成功,删除不进入回收站

String[] names = f.list(); // 获取当前目录下所有一级文件名
File[] files = f.listFiles(); // 获取当前目录下所有一级文件对象,包含隐藏文件
// 路径不存在、文件、无权限返回null
// 空文件夹,返回长度为0数组

f.getAbsoluteFile(); // 获取绝对路径

线程

创建线程

  1. 继承Thread类
// 创建线程
class zThread extends Thread {
    @Override
    public void run() {
        System.out.println("thread");
    }
}

Thread t1 = new zThread();
t1.start(); // 开启子线程子线程和主线程同时跑
  1. 实现Runnable接口
class zRunnable implements Runnable {
    @Override
    public void run() {

    }
}

// 创建线程任务类
Runnable r = new zRunnable();
// 将线程任务对象交给线程对象处理
Thread t = new Thread(r);
t.start(); // 启动
  1. 实现Callable接口,重写call方法,解决线程无法返回结果,Callable类型对象封装成FutureTask,线程任务对象,再交给Thread对象,start启动,get拿结果
  • 主线程发现第一个线程未执行完,会由CPU等一个线程执行完后,才往下执行
class zCallable implements Callable<Integer> {
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++){
            System.out.println(i);
            sum += i;
        }
        return sum;
    }
}

Callable<Integer> c = new zCallable();
FutureTask<Integer> f = new FutureTask<>(c); // 本身是Runnable线程任务对象
Thread t = new Thread(f);
t.start();

t.get(); // 获取返回值

线程方法

Thread t = new zThread();
t.setName("aa"); // 设置线程名字
t.getName(); // 获取线程名字
System.out.println(Thread.crrentThread().getName()); // 输出当前线程名字,以区分

Thread.sleep(1000);// 线程休眠1s

// join方法 让调用这个方法的线程先执行完
t.join(); // 插队 让t线程先执行完毕再执行主线程

线程安全

多线程操作同一个共享资源

方法一:同步代码块

  • 把访问共享资源的核心代码上锁,每次只允许一个线程加锁后进入,执行完毕自动解锁
  • 建议使用共享资源为锁对象,实例方法建议使用this,静态方法用字节码(类名.class对象)
synchronized(同步锁) { // 同时执行的线程:同步锁必须是同一对象 (this)
	访问共享资源
}

方法二:同步方法

  • 把访问共享资源的核心方法上锁
  • 默认用锁:实例方法使用this,静态方法用字节码(类名.class对象)
修饰符 synchronized 返回值类型 方法名称(形参列表) {
	操作共享资源代码
}

方法三:Lock 锁

  • JDK5提供,创建锁对象加锁解锁
  • Lock是接口,不可直接实例化,采用实现类ReentrantLock来创建Lock锁对象
private final Lock lk = new ReentrantLock();
lk.lock() // 上锁
lk.unlock() // 解锁 放在finally中

线程池

  • 复用线程
  • JDK5提供接口ExecutorService的实现类:ThreadPoolExecutor自创建一个线程池对象
  • 使用Executors线程池工具类调用方法返回不同特点的线程池对象
ThreadPoolExecutor(int corePoolSize, // 核心线程数量
	int maximumPoolSize, // 最大线程数量
	long keepAliveTime, // 临时线程的存活时间,空闲多久被消灭
	TimeUnit unit, // 指定临时线程存活的时间单位,秒分时天
	Blocking Queue<Runnable> workQueue, // 线程池任务队列
	ThreadFactory threadFactory, // 线程池的线程工厂,招聘线程的 HR
	RejectedExecutionHandler handler) // 任务拒绝策略:线程都在忙,任务队列也满,新任务来如何处理
	

临时线程创建:新任务提交,核心线程都在忙,任务队列满,可创建临时线程

ExecutorService pool = new ThreadPoolExecutor(xx, xx, xx);

pool.execute(Runnable command); // 执行任务
pool.shutdown(); // 等全部任务执行完关闭线程池
pool.shutdownNow(); // 立刻关闭线程池停止正在执行的任务

任务拒绝策略

图片无法加载

Callable: Future<T> submit(Callable<T> task); // 执行,返回未来任务对象,用于获取线程返回结果

public class myCallable implements Callable<String>{
    private int n;
    public myCallable(int n)
    {
        this.n = n;
    }

    public String call() throws Exception {
        int sum = 0;
        for(int i = 0; i <= n; i++){
            sum += i;
        }
        return  Thread.currentThread().getName() + "计算1-" + n + "的和是" + sum;
    }
}

Future<String> f1 = pool.submit(new myCallable(100));
System.out.println(f1.get());

Executors创建线程池

  • 静态方法返回不同特点的线程池对象
public static ExecutorService newFixedThreadPool(int nThreads)创建固定线程数量线程池,若某线程因执行异常结束,则线程池会补充一个新线程替代他
public static ExecutorService newSingleThreadExecutor()创建只有一个线程的线程池对象,线程异常结束则补充一个新线程
public static ExecutorService newCachedThreadPool()线程数随任务增加而增加,若线程任务执行完毕,空闲60s则被回收掉
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个线程池,在给定延迟后运行任务,定期执行任务

网络编程

InetAddress

InetAddress ip = InetAddress.getLocalHost(); // 获取本机IP对象
InetAddress ip2 = InetAddress.getByName("www.baidu.com"); // 获取IP对象

ip.getHostName(); // 域名
ip.getHostAddress(); // 优先公网IP
ip2.isReachable(5000); // 判断本机与对方主机是否相通

BigDecimal 解决浮点型运算时结果失真问题

double a = 0.1;
BigDecimal a1 = BigDecimal.valueOf(a);
BigDecimal b1 = BigDecimal.valueOf(b);
BigDecimal c1 = a1.add(b1); // divide
double res = c1.doubleValue();

反射

反射:加载类,允许以编程方式解剖类中成员变量、方法、构造器等

  1. 加载类,获取类的字节码:Class 对象

    // 获取类的对象
    Class c = 类名.class; // 打印为包到类名
    
    Class c = forName("package_name类的全类名(xx.xx.xx)");
    
    Class c = 对象.getClass();
    
    c.getName(); // 全类名
    c.getSimpleName(); // 类名
  2. 获取类的构造器:Constructor 对象

    Constructor[] cons = c.getDeclaredConstructors(); // 获取全部构造器(public)
    cons.getParameterCount(); // 获取参数个数
    
    Constructor con = c.getDeclaredConstructor(); // 无参构造器
    Constructor con = c.getDeclaredConstructor(String.class, int.class); // 2个参数的有参构造器
    
    // 创建对象
    // 暴力反射,可访问私有构造器、方法、属性
    con.setAccessible(true); // 绕过访问权限
    Class_name d = (Class_name) con.newInstance(arg1, arg2, ...);
  3. 获取类的成员变量:Field 对象

    Field[] fields = c.getDeclaredFields(); 
    
    Field field = c.getDeclaredField("xxx"); // 获取单个成员变量对象
    field.getType.getName();
    
    // 获取成员变量进行取值赋值 对象d
    field.setAccessible(true); // 暴力访问private
    field.set(d, "xxx"); // 设置该成员变量
    type xxx = (type) field.get(d); // 获取成员变量
  4. 获取类的成员方法:Method 对象

    Method[] methods = c.getDeclaredMethods();
    
    Method a = c.getDeclaredMethod("func"); // 无参数func方法
    Method a = c.getDeclaredMethod("func", String.class); // 有参数func方法
    
    // 调用方法 对象d
    a.setAccessible(true);
    Object res = a.invoke(d, arg1, arg2, ...);

绕过泛型约束

当限制了List<String>时,可通过反射向其中写入其他类型数据

注解

  • 特殊标记:@Override,让其他程序根据注解信息来决定如何执行该程序
  • 可用在类上、构造器上、方法上、成员变量上、参数上

自定义注解Annotation

  • 特殊属性名value,若注解中只有一个value属性,使用注解时,value名可不写
public @interface 注解名 {
	public 属性类型 属性名() default 默认值;
}
public @interface A {
	String name();
	int age() default 18;
	String[] address();
	// 当只有 String value() 以下改为 @("xxx")
}

@A(name="aa", age=18, address= {"北京", "上海"})

元注解

用于注解注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface B ... 
图片无法加载

解析

  • 判断类、方法、成员变量上是否有注解,将注解里内容解析出来
  • Clas、Method、Field、Constructor都实现了AnnotatedElement接口,可解析注解
Class c = Demo.class;
if(c.isAnnotationPresent(TestDemo.class) {// 判断类上是否有注解TestDemo
	TestDemo testdemo = (TestDemo)c.getDeclaredAnnotation(TestDemo.class);
	
	// 获取值
	xxx = testdemo.xxx();
}

动态代理

使用 java.lang.reflect.Proxy 类为对象创建代理对象

public static Object newProxyInstance(
	ClassLoader loader, // 指定用哪个类加载器,加载生成的代理类
	Class<?>[] interfaces, // 指定接口,这些接口用于指定生成的代理长啥样,有哪些方法
	InvocationHandler h) // 指定生成的代理对象做什么事情

public class ProxyUtil{
	public static StarService createProxy(Star s){
		StarService proxy = (StarService) Proxy.newProxyInstance(
			ProxyUtil.class.getClassLoader(),
			s.getClass().getInterfaces(),
			new InvocationHandler() {
				@Override
				public Object invoke(
					Object proxy, // proxy接收到代理对象本身
					Method method, // 正在被代理的方法
					Object[] args) throws Throwable { // 正在被代理方法的参数
						Object result = method.invoke(s, args);
						return result;
				}
			}
		);
	}
}

框架

Commons-io

  • IO框架,框架:类、接口编译为class形式,压缩为.jar结尾文件发出去

  • commons-io-2.6.jar,项目新建lib文件夹,复制到其中,jar文件右键:Add as Library

  • 类中导包使用

FileUtils.copyFile(new File("a"), new File("b")); // 复制文件
FileUtils.deleteDirectory(new File("a")); // 删除文件夹

// JDK
Files.copy(Path.of("x"), Path.of("b")); // 已存在则报错

单元测试

  • Junit:单元测试框架
import org.junit.Test

public class XXTest {
	// public, 无参, 无返回值 选中测试方法右键"Junit运行"
	@Test
	public void testFunc(){
		...
		// 断言结果与预期是否一致
		Assert.assertEquals("test failed", expected_value, test_value);
	}
}