Java SE
JDK
开发者工具包,配置环境变量,JDK = JRE + java的开发工具
JRE
运行环境 JVM + 核心类库
基础语法
注释
- 行内注释 //
- 多行注释 /**/SE
- 文档注释 /***/ javadoc 生成帮助文档
标识符
关键字:
数据类型
- 基本数据类型
- 整数 byte 1 , short 2,int(默认) 4,long 8
0b二进制 0x 十六进制 o 八进制
- 浮点数 float 4,double (默认)8,BigDecimal(银行)
- 字符 char 2
ascii,utf-8,Unicode ,GBK
- 布尔值 boolean 1
2.引用数据类型
- 类
- 接口
- 数组
类型转换
- 自动类型转换
低转高,子类转父类
- 强制类型转换
高转低 (低)高
变量与常量
作用域:
- 类变量 static
- 实例变量
- 局部变量 (成员变量)
常量:
final MAX=10
命名规范:
- 见名知意
- 驼峰法(变量,方法)
- 类,首字母大写,驼峰命名
- 常量:大写+下划线
- 不要使用拼英命令
运算符
算术运行符:+ - * / % ++ –
赋值运算符:=
关系运算符:> < >= <= != instanceof
逻辑运算符:&& || !
位运算符:$ | ^ ~ >> << >>>
条件运算符:?: 高频
扩展运算符:+= - = *= /+
包机制
域名倒写
防止命名冲突
package 第一行
import
JavaDoc
JDK帮助文档
- javadoc
- @author
- @Version
- @Since
- @param
- @return
- @throws
流程控制
顺序结构
默认的结构,至上而下。
选择结构
-
if单选择结构
-
if -else 双选择结构
-
if - else if else 多选择结构
-
switch
jdk 支持了String 类型 (反编译可以看到)
case穿透现象 (不加break)
default
break
循环结构
- while
- do…while
- for
- 增强for循环
break & continue
break:跳出循环
continue:终止当次循环
return:结束方法的运行
方法
方法的定义:修饰符 返回值 方法名(参数名){return 返回值;}
方法的调用:
- 类名.方法 static
- 对象.方法
方法的重载:名字相同,参数列表不同。
命令行传参:给main方法传递参数 (JVM调优)
可变参数:… 必须放在最后一个参数(int… arg)
递归:自己调用自己,给自己一个出口
数组
数组的定义:必须是同一个数据类型。
数组的使用:通过下标拿到值,增强for循环遍历
二维数组:int [] []
Arrays工具类:
数组转集合
/**
*遍历
***/
int arrs[] = {1, 2};
List<Integer> list = new ArrayList<>();
for (int ele : arrs) {
list.add(ele);
}
System.out.println(list);
/**
*使用数组工具类的asList()方法
*如果传入的参数是一个数组,那么这个数组一定要是引用类型才能将其转换为List集合,
*当传入基本数据类型数组时则会将这个数组对象当成一个引用类型对象存进List集合
**/
List list = Arrays.asList(arry);
排序算法:
- 冒泡
- 选择
- 插入
- 快速
- 归并
- 希尔
- 堆
- 基数
面向对象
什么是面向对象
类与对象
- 类是对象的模板:模板Class
- 对象是类的具体
构造方法
方法的重载,默认的无参构造。如果手动定义了有参构造必须要手动再加一个无参构造,单例模式,需要构造器私有。
new对象
栈存放引用,堆存放具体的对象。
三大特性
封装
属性私有,get,set
继承
extends
Object 所有的类都继承自Object
子类拥有父类的全部特性
方法重写
this
super
java是单继承,只能继承一个父类
多态
父类的引用指向子类的对象
Person person = new Student();
instanceof 关键字,如果匹配,可以进行类型之间的转换
修饰符
- public
- protected
- private
- static
- final
- abstract
接口
interface
约束,只能定义方法名
子类实现接口,必须重写其中的方法
只有一个方法的接口叫做函数式接口,可以使用lambda表达式简化。
接口比抽象类更抽象
一个类可以实现多个接口
内部类
-
静态内部类
-
局部内部类
-
匿名内部类(重点)
异常
Throwable
Exception:运行时异常。
- 1/0
- ClassNotFound
- NullPoint
- UnKownType
- 下标越界异常
Error:
- AWT错误
- JVM错误
- StackOverFlow 栈溢出
- OutOfMemory 内存溢出
五个关键字
- try
- catch 先小后大
- finally
- throw 手动抛出异常
- throws 方法抛出异常
自定义异常
继承Exception类即可
常用类
Object
- hashCode()
- toString()
- clone()
- getClass()
- notify()
- wait()
- equals()
Math
常见的数学运算
Random
- 生成随机数
new Random().nextInt(10); //产生0~9之间的数字
- UUID 随机生成字符串
UUID.randomUUID();
File
创建文件
查看文件
修改文件
删除文件
包装类
对基本数据类型进行包装,包装成一个对象。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
long | Long |
int | Integer |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
装箱操作
基本数据类型变成包装类,以Integer为例
new Integer('a');
Integer.valueOf('a');
拆箱操作
把包装类的对象转换位对应的基本数据类型的变量
Integer integer = new Integer(1);
int i = integer.intValue();
System.out.println(i);
自动拆箱与自动装箱
可以直接把一个包装类对象赋值给基本数据类型的变量,以及可以把基本数据类型的变量赋值给包装类。
String【与基本数据类型,包装类之间转换】
- String转成包装类
new Integer("123");
字符串当中必须都是数字或者为单个字符。
- String转成基本数据类型,字符串转boolean,除了true,其它的都为false。
包装类.parseInt("字符串");
- 包装类转换为String
Integer integer = new Integer("1235");
String s = integer.toString();
System.out.println(s);
- 基本数据类型转字符串,直接再后面加+""
总结
- 默认值 int为0 ,Integer为null
- 集合框架当中不能存放基本数据类型,只能存放对象
- 一般类中的成员变量使用包装类(堆中),方法中使用数据类型(栈中)。
Date
- Date
- SimpleDateFormat
- Calendar 建议使用
String
String 的实现与其方法
以主流的 JDK 版本 1.8 来说,String 内部实际存储结构为 char 数组,源码如下:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}
String 源码中包含下面几个重要的方法:
- 多构造方法
String 字符串有以下 4 个重要的构造方法:
// String 为参数的构造方法
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
// char[] 为参数构造方法
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
// StringBuffer 为参数的构造方法
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
// StringBuilder 为参数的构造方法
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}}
- equals() 比较两个字符串是否相等
源码如下:
public boolean equals(Object anObject) {
// 对象引用相同直接返回 true
if (this == anObject) {
return true;
}
// 判断需要对比的值是否为 String 类型,如果不是则直接返回 false
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
// 把两个字符串都转换为 char 数组对比
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 循环比对两个字符串的每一个字符
while (n-- != 0) {
// 如果其中有一个字符不相等就 true false,否则继续对比
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String 类型重写了 Object 中的 equals() 方法,equals() 方法需要传递一个 Object 类型的参数值,在比较时会先通过 instanceof 判断是否为 String 类型,如果不是则会直接返回 false,当判断参数为 String 类型之后,会循环对比两个字符串中的每一个字符,当所有字符都相等时返回 true,否则则返回 false。
还有一个和 equals() 比较类似的方法 equalsIgnoreCase(),它是用于忽略字符串的大小写之后进行字符串对比。
- compareTo() 比较两个字符串
compareTo() 方法用于比较两个字符串,返回的结果为 int 类型的值,源码如下
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
// 获取到两个字符串长度最短的那个 int 值
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
// 对比每一个字符
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
// 有字符不相等就返回差值
return c1 - c2;
}
k++;
}
return len1 - len2;
}
从源码中可以看出,compareTo() 方法会循环对比所有的字符,当两个字符串中有任意一个字符不相同时,则 return char1-char2。比如,两个字符串分别存储的是 1 和 2,返回的值是 -1;如果存储的是 1 和 1,则返回的值是 0 ,如果存储的是 2 和 1,则返回的值是 1。
还有一个和 compareTo() 比较类似的方法 compareToIgnoreCase(),用于忽略大小写后比较两个字符串。
可以看出 compareTo() 方法和 equals() 方法都是用于比较两个字符串的,但它们有两点不同:
-
equals() 可以接收一个 Object 类型的参数,而 compareTo() 只能接收一个 String 类型的参数;
-
equals() 返回值为 Boolean,而 compareTo() 的返回值则为 int。
它们都可以用于两个字符串的比较,当 equals() 方法返回 true 时,或者是 compareTo() 方法返回 0 时,则表示两个字符串完全相同。
- 其他重要方法
- indexOf():查询字符串首次出现的下标位置
- lastIndexOf():查询字符串最后出现的下标位置
- contains():查询字符串中是否包含另一个字符串
- toLowerCase():把字符串全部转换成小写
- toUpperCase():把字符串全部转换成大写
- length():查询字符串的长度
- trim():去掉字符串首尾空格
- replace():替换字符串中的某些字符
- split():把字符串分割并返回字符串数组
- join():把字符串数组转为字符串
- toCharArray():将字符串转化为字符数组
- charAt(): 返回第n个字符
考点分析
String 源码属于所有源码中最基础、最简单的一个,对 String 源码的理解也反应了你的 Java 基础功底。
知识扩展
- == 和 equals 的区别
== 对于基本数据类型来说,是用于比较 “值”是否相等的;而对于引用类型来说,是用于比较引用地址是否相同的。
查看源码我们可以知道 Object 中也有 equals() 方法,源码如下
public boolean equals(Object obj) {
return (this == obj);
}
可以看出,Object 中的 equals() 方法其实就是 ==,而 String 重写了 equals() 方法把它修改成比较两个字符串的值是否相等。
- final 修饰的好处
从 String 类的源码我们可以看出 String 是被 final 修饰的不可继承类,源码如下:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
}
String继承了Comparable接口,实现了其compareTo()方法。
Java 语言之父 James Gosling 的回答是,他会更倾向于使用 final,因为它能够缓存结果,当你在传参时不需要考虑谁会修改它的值;如果是可变类的话,则有可能需要重新拷贝出来一个新值进行传参,这样在性能上就会有一定的损失。
James Gosling 还说迫使 String 类设计成不可变的另一个原因是安全,当你在调用其他方法时,比如调用一些系统级操作指令之前,可能会有一系列校验,如果是可变类的话,可能在你校验过后,它的内部的值又被改变了,这样有可能会引起严重的系统崩溃问题,这是迫使 String 类设计成不可变类的一个重要原因。
总结来说,使用 final 修饰的第一个好处是安全;第二个好处是高效,以 JVM 中的字符串常量池来举例,如下两个变量:
String s1 = "java";
String s2 = "java";
只有字符串是不可变时,我们才能实现字符串常量池,字符串常量池可以为我们缓存字符串,提高程序的运行效率,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L6JYUtXL-1590655932951)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200514180441735.png)]
试想一下如果 String 是可变的,那当 s1 的值修改之后,s2 的值也跟着改变了,这样就和我们预期的结果不相符了,因此也就没有办法实现字符串常量池的功能了。
- String 和 StringBuilder、StringBuffer 的区别
因为 String 类型是不可变的,所以在字符串拼接的时候如果使用 String 的话性能会很低,因此我们就需要使用另一个数据类型 StringBuffer,它提供了 append 和 insert 方法可用于字符串的拼接,它使用 synchronized 来保证线程安全,如下源码所示:
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
因为它使用了 synchronized 来保证线程安全,所以性能不是很高,于是在 JDK 1.5 就有了 StringBuilder,它同样提供了 append 和 insert 的拼接方法,但它没有使用 synchronized 来修饰,因此在性能上要优于 StringBuffer,所以在非并发操作的环境下可使用 StringBuilder 来进行字符串拼接。
-
StringBuffer
可变长,append() ,多线程数据量较大,效率低,安全。
-
StringBuild
可变长,单线程数据量较大,效率高,不安全。
- String 和 JVM
String 常见的创建方式有两种,new String() 的方式和直接赋值的方式,直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;而 new String() 的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串,如下代码所示:
String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
它们在 JVM 存储的位置,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nC3HZYsr-1590655932958)(C:\Users\游建成\AppData\Roaming\Typora\typora-user-images\image-20200514180456516.png)]
集合框架
集合与数组的区别
- 数组能存基本数据类型和引用数据类型。
- 集合只能存放引用数据类型,如果直接放也会自动装箱(把基本数据类型转成对象)
- 数组长度固定,不能再去增长,集合长度是可以改变的,根据元素的增长而增长。
集合转数组
-
````java
/**
*1.遍历
***//**
*2.使用集合的toArray()方法
**/
public class testListToArray {
public static void main(String[] args) {
List list = new ArrayList();
list.add(“刘雯”);
list.add(“杨紫”);
list.add(“胡歌”);
String [] strArr = list.toArray(new String[]{});
System.out.println(Arrays.toString(strArr));//[刘雯, 杨紫, 胡歌]
}
}/**
*toArray源码
**/
public T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a’s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Collection
List
有序可重复。
有角标,可以根据角标来去添加元素,角标index必须得要<=size,大于size就会报错
c1.add(1,"zs");
获得指定角标得元素。
c1.get(1);
遍历方式:3种
ArrayList<Integer> integers = new ArrayList<>();
integers.add(12);
integers.add(34);
integers.add(12243);
/**
* 数组方式遍历
* **/
for (int i = 0; i < integers.size(); i++) {
System.out.println(integers.get(i));
}
/**
* 增强for循环遍历
* **/
for (Integer integer : integers) {
System.out.println(integer);
}
/**
* 迭代器遍历
* **/
Iterator<Integer> iterator = integers.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
-
ArrayList(数组实现)
常用方法:add,get,remove,contains,size,clear,isEmpty
集合转数组:toArray();
-
LinkedList(链表实现)
插入删除快速,查询,修改比较慢
特有:
- 往第一个位置添加/删除元素 addFirst() removeFirst
- 在集合的最后一个位置添加/删除元素 addLast() removeLast()
- Vector (数组实现)
更加安全,synchronized方法加锁
- Stack
Set
无序不可重复
- HashSet (Hash算法)
自定义对象添加到HashSet,需重写hashCode()与equals()方法。
添加一个对象时,会调用对象的父类Object的hashCode方法,对象和集合当中的hashCode不相同,就不会调用equals方法,直接添加元素。当hashCode相同时,会调用equals。如果equals返回为true,就不会添加到集合当中。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass())
return false;
Student student = (Student) o;
return id.equals(student.id) &&name.equals(student.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
- TreeSet (二叉树)
当中存放的类型必须得同一类型,自定义的对象不能直接放到TreeSet,想要添加到TreeSet当中必须要遵循一定的规则
- 实现Comparable接口
- 覆盖当中的compareTo方法。
迭代器
每一个类当中都有自定义的迭代方法,如下遍历集合。
public class Test9 {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student(1,"zs"));
students.add(new Student(2,"lisi"));
students.add(new Student(3,"wangwu"));
/**
放到Iterator内容会自动类型提升为Object
若要调用方法,需要强转
添加泛型之后 可以省去 强转了行的麻烦
**/
Iterator<Student> iterator = students.iterator();
while (iterator.hasNext()){
//获取当前的游标,再往右走一位
//添加泛型之后 可以省去 强转了行的麻烦
Student next = iterator.next();
System.out.println(next);
}
}
}
public class Student {
private Integer id;
private String name;
}
Iterator的方法:
boolean hasNext();
default void remove() {.....}
E next();
default void forEachRemaining(Consumer<? super E> action) {......}
并发修改异常,再迭代集合的过程中,是不允许直接修改集合结构,删除时可以使用迭代当中的删除方法。
在List当中有自己特有的迭代器,Set中无,类中多个几个方法,如添加元素remove
boolean hasPrevious();
E previous();
int nextIndex();
void remove();
int previousIndex();
Map
HashMap(重点)
基本操作:
public static void main(String[] args) {
HashMap<Integer, Student> studentHashMap = new HashMap<>();
studentHashMap.put(1,new Student(1,"zs"));
studentHashMap.put(2,new Student(2,"lisi"));
studentHashMap.put(3,new Student(3,"wangwu"));
//获取元素
Student student = studentHashMap.get(1);
System.out.println(student);
//遍历元素
Set<Integer> integers = studentHashMap.keySet();
for (Integer integer : integers) {
Student student1 = studentHashMap.get(integer);
System.out.println(student1);
}
for (Integer integer : studentHashMap.keySet()) {
Student student1 = studentHashMap.get(integer);
System.out.println(student1);
}
/**
*获取所有的key-value对象的entry对象
* entry是定义在map内部当中的一个接口**/
Set<Map.Entry<Integer, Student>> entries = studentHashMap.entrySet();
for (Map.Entry<Integer, Student> entry : entries) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
}
结构:
JDK1.7:数组+链表
JDK1.8:数组+链表+红黑树
TreeMap
与TreeSet类似,对key进行排序。
HashTable
Collections工具类
void reverse(List list)//反转
void shuffle(List list)//随机排序
void sort(List list)//按自然排序的升序排序
void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)//交换两个索引位置的元素
Collections.sort(list); //对集合排序
泛型
使用泛型意味着编写的代码可以被很多不同的类型的对象所重用。
使用泛型的好处:
- 提高了安全性
- 省去了强转的麻烦。
向上转型与向下转型
package cn.dali.code24;
/**向上转型:父类引用指向子类对象
* 格式:父类名称 对象名 = new 子类名称();
* 向下转型:当我们使用父类引用指向子类对象的时候,我们想把父类对象转为子类对象时,需要向下转型
* 向下转型就是一个还原动作。
*
* 格式:子类名称 对象名 = (子类名称) 父类对象
*
* 注意:一个父类有多个子类,所以当我们转型的时候要注意,创建对象的时候是用的哪个子类。
*
* 判断方法:instanceof关键字
* 用法:父类对象 instanceof 子类名称,其返回值是一个布尔值,
* 如果是用该子类创建的为true,否则为false;
* */
public class Demo01 {
public static void main(String[] args) {
Person person = new Student();//向上转型
Person person1 = new Teacher();//向上转型
// Student a = (Student) person;//正确写法
// Student b = (Student) person1;//错误写法,创建person01时,用的是Teacher类
if(person instanceof Student){
Student a = (Student) person;
}
if(person instanceof Teacher){
Teacher a = (Teacher) person;
}
}
}
广泛通用的类型
一开始还不确定是什么类型,使用时才确定,代码模板中类型不确定,谁调用该段代码,谁就可以指明这个类型。使用步骤:
- 在类的后面加。
- 在变量的前面添加上一个T
注意:T不能是基本数据类型,必须得要引用类型,创建对象没有指明泛型类型,它就是Object
- 泛型类
在类上面定义的泛型,在创建对象的时候,需指明泛型的类型,泛型当中定义的泛型只能用在普通方法上面,不能使用在静态方法上面,静态方法是直接使用类名调用的,泛型是在创建对象的时候才会去指定类型。
- 泛型方法
就是在方法上面添加了泛型,单独对一个方法上面声明泛型,方法当中的泛型是在使用方法时,参数传递具体的类型。
public class A<T> { // 泛型类:定义类的时候指定类型形参T,在类里面T就可以当成类型使用
private T a;
public T getA() {
return a;
}
public void setA(T a) {
this.a = a;
}
}
通配符
不知道使用什么类型来接受的时候,可以使用?表示未知。
- 泛型的上限
用来限定元素的类型必须是指定类型的子类。
List<? extends Number> //最高Number指定类型
- 泛型的下限
用来限定元素的类型必须是指定类型的父类
List<? super Number> //最低Number
IO流
字节流
输出:OutputStream
输入:InputStream
字符流
Reader
Writer
节点流
- CharArrayReader,InputStream,OutputStream
- StringReader
- Pipe(管道流) PipedOutputStream
- File
处理流
- Buffered
- BufferInputStream
- BufferOutputStream
- BufferedReader
- BufferedWriter
- data
- DataInputStream
- DataOutputStream
- 转换流
- InputStreamReader
- OutputStreamWriter
-
过滤流 4个
-
print
- PrintWriter
- PrintStream
-
Object流
-
序列化与反序列化
多线程
进程与线程
进程是执行程序的一次执行过程,是一个动态的概念,由系统分配,一个进程有多个线程,线程的调度由CPU决定
创建的方法
-
Thread
start0,本地方法,java无权调用,交给底层的c处理
private native void start0();
-
Runable
函数式接口 lambda
-
Callable
可以有返回值
静态代理
真实对象和代理对象要实现同一个接口,代理对象要代理真实角色。
好处:
代理对象可以做很多真实对象做不了的事情,真实对象可以专注做自己的事情。
/**
* @Author you猿
* @Date 2020/5/17 9:27
*/
public class StaticProxy {
public static void main(String[] args) {
new MarriageCompany(new You()).happyMarry();
}
}
interface Marry{
public void happyMarry();
}
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("marry me");
}
}
class MarriageCompany implements Marry{
private Marry marry;
public MarriageCompany(Marry marry) {
this.marry = marry;
}
@Override
public void happyMarry() {
before();
this.marry.happyMarry();
after();
}
private void after() {
System.out.println("marry after");
}
private void before() {
System.out.println("marry before");
}
}
Lambda
函数式编程,避免内部类定义过多,手动推导。
实现类 =》静态内部类 =》局部内部类 =》匿名内部类 =》lambda
总结:
lambda表达式只有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹。
前提是接口为函数式接口
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号。
线程的状态
- 新建
- 就绪
- 运行
- 阻塞
- 死亡
线程停止
建议线程正常停止,利用次数,不建议死循环。
建议使用标志位,设置一个标志位。
不要使用stop或者destory等过时或者JDK不建议使用的方法。
常用的方法
-
sleep 延时休眠,每个对象都有一把锁,sleep不会释放锁。
/** * @Author you猿 * @Date 2020/5/17 10:52 * 倒计时10秒 */ public class Sleep { public static void main(String[] args) { Date date = new Date(System.currentTimeMillis()); int times = 0; while (true){ try { if (times == 10) break; String format = new SimpleDateFormat("HH:mm:ss").format(date); System.out.println(format); Thread.sleep(1000); date = new Date(System.currentTimeMillis()); times++; } catch (InterruptedException e) { e.printStackTrace(); } } } }
-
join 线程强制执行,类似于插队
-
yield 礼让,礼让不一定成功,看CPU。
-
isLive 是否存活
-
start 启动
-
get/setPriority 设置优先级,主线程默认优先级为5,范围1~10。
-
interrupt 终断线程
守护线程
正常的线程默认都为用户线程。
//开启守护线程
thread.setDaemon(true);
线程同步(抢票问题)
多个对象操作同一个资源,并发。
前提:队列+锁
锁的对象应该是要修改的对象。
synchronied:
1. 同步方法,弊端,锁太多了,默认锁的对象为this。
2. 同步代码段,常用,可以锁任何对象。
第一个线程进来拿到锁,后面的就要排队了,直到这个人释放锁,后面拿到锁才能进去。
死锁
两个人都抱着对方的锁。
条件:
1.互斥:一个资源每次只能被一个进程使用
2.请求与保持:一个进程因请求资源而阻塞,对已获得的资源保持不放。
3. 不剥夺条件:一个进程已经获得了资源,在这个线程没有释放不能强行把资源拿走。
4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
解决方法:用完了及时释放锁。
Lock(JDK5.0)
synchronized与lock的对比:
- Lock是显示锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放。
- Lock只有代码块锁,synchronized有代码块和方法锁。
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性。
- 优先使用顺序:lock > 同步代码块 > 同步方法
优先级最高,可重入锁。
ReentrantLock
- lock 上锁
- trylock
- unlock 解锁
线程通讯
java提供了几个方法解决线程之间的通信问题。
方法名 | 作用 |
---|---|
wait() | 表示线程一直等待,直到其它线程通知,与sleep不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程优先调度 |
生产者与消费者
缓冲区:消息队列
标志位:信号灯法
/**
* @Author you猿
* @Date 2020/5/17 17:29
*/
public class Semaphore {
public static void main(String[] args) {
TV tv = new TV();
new Actor(tv).start();
new Audience(tv).start();
}
}
class Actor extends Thread{
private TV tv ;
Actor(TV tv ){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 200; i++) {
if (i%2 == 0){
tv.play("天天向上");
}else {
tv.play("广告");
}
}
}
}
class Audience extends Thread{
private TV tv ;
Audience(TV tv ){
this.tv = tv;
}
@Override
public void run() {
while (true){
tv.watch();
}
}
}
class TV {
private String voice;
private Boolean flag = true;
public synchronized void play(String voice){
if (!this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("播放了"+voice);
this.voice = voice;
this.flag = !this.flag;
this.notifyAll();
}
public synchronized String watch(){
if (this.flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("看完了"+this.voice);
this.flag = !this.flag;
this.notifyAll();
return voice;
}
}
线程池
JDK5.0起提供了线程池相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- execute() 执行任务/命令,无返回值,一般用于执行Runnable
- submit() 执行任务,有返回值,一般用于执行Callable
- shutdown() 关闭连接池
Executors:工具类 ,线程池的工厂类,用于创建并返回不同类型的线程池。
/**
* @Author you猿
* @Date 2020/5/17 18:17
*/
public class Executorss {
public static void main(String[] args) {
//创建服务,创建线程池
//参数为线程池的大小
ExecutorService executorService = Executors.newFixedThreadPool(10);
//执行
executorService.execute(new MyThread1());
executorService.execute(new MyThread1());
executorService.execute(new MyThread1());
executorService.execute(new MyThread1());
//关闭连接
executorService.shutdown();
}
}
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
池化技术
池的大小
最大连接数
保持时间
网络编程
IP
端口
Socker编程
TCP:
三次/四次握手,面向连接。
UDP:无连接,Packet
URL
初始Tomcat
聊天通信
文件上传
注解与反射
注解
原注解
内置注解
自定义注解
public class Test10 {
@MyAnnotation(name = "youyuan",age = 9,id = 0)
public void test(){
}
}
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
//自定义注解
@interface MyAnnotation{
//注解的参数:参数类型 + 参数名()
String name() ;
int age()vdefault 0 ;
int id() default -1;
}
反射
Class
类加载机制
Method
Field
Construct:newInstance(),获取的时候需要,传递参数的class类型
破坏私有关键字:setAccesssible(true);
性能分析:正常 > 检测关闭的反射> 默认的反射
反射获得注解,泛型…