一.Map集合
将键映射到值得对象,键得值不能重复,一个键可以对应最少一个值。
Map接口下的封装好得实现类
–|HashMap
–|TreeMap
1.Map接口下的方法
增:
//将指定得值与该映射中得指定键相关联
V put(k key,v value)
//将指定地图的所有映射复制到此映射
v putAll(Map<? extends k,? extends v> m)
删:
//通过键删除指定的值 返回值是被删除的值
v remove(k key)
改:
//当键没有的话,就是添加,没有的话,就是覆盖
V put(K key, V value)
查:
int size(); // 查看集合中的元素的个数
boolean isEmpty(); //判断集合是否为空
//判断集合中是否包含这个键
boolean containsKey(Obejct Key);
//判断集合中是否包含这个值
boolean containsValue(Obejct value);
重要方法:
v get(k key)//通过键获取值
//Set<String> strings = map.keySet();
set<k> keyset()//获取所有的键存到set集合中
//Collection<String> values = map.values();
Collection<V> values() 获取所有集合中的值
//Set<Map.Entry<k,v>> set = map.entrySet();
Set<Map.Entry<K,V>> entrySet() //将键值对 的map的实体存到set集合中
2.Map集合的value值可以放对象
格式:
class Person{
}
main{
Map<Integer,Person> map = new HashMap<>();
map.put(1,new Perspn("he",18));
sout(map.get(1).getName())//he
}
3.Map集合可以用LIst一键对多值
Map<Integer, List<String>> map = new HashMap<>();
List<String> list = new ArrayList<>();
list.add("旺财");
list.add("狗蛋");
list.add("常威");
map.put(1,list);
List<String> list1 = new ArrayList<>();
list1.add("来福");
list1.add("坤坤");
list1.add("存爱");
map.put(2, list1);
System.out.println(map);
}
二.File类
相对路径:
得有一个参照物
./代表的是当前的工作目录
…/上一级目录
…/…/上两级目录
…/…/…/上三级目录
绝对路径:
从磁盘的跟目录一直到文件的所在位置
D:/aaa/1.txt
1.File的构造方法
格式1:
File file = new File("D:/aaa/1.txt");
格式2:
File file = new File("c:/aaa", "1.txt");
格式3:
File file = new File("C:\\aaa\\1.txt")
2.File下的方法
增:
boolean createNewFile();//新建一个文件
boolean mkdir();//创建单级目录
boolean mkdirs();//创建多级目录
删:
boolean delete();//删除文件或文件夹
查:返回值boolean
boolean isFile()://是否是文件
boolean isDirectory();//是否是文件夹
boolean isHidden();//是否是隐藏文件
boolean isAbsolute();//是否是绝对路经
boolean exist();//文件或文件夹是否存在
查:返回值String
String getName();//获取文件或着文件夹的名字
String getParent();//获取上一级目录
String getPath();//获取File对象的绝对路径
查:返回值long
long length();//获取文件占用磁盘的字节数
long lastModified();//获取文件修改时间 时间戳
查:获取当前文件夹下面的所有的文件
File[] listFiles();
File[] files = file.listFiles();
String list();
String[] list = file.list();
三.递归
一个方法多次调用自己 , 但是得有结束得条件在递归第哦啊用的过程中 , 系统为每一层的返回点或者局部变量开辟了栈来存储 .
//求1-100的和,使用递归的方式来写
public static void main(String[] args) {
System.out.println(sum(1));
}
public static int sum(int num){
if(num == 101){
return 0;
}
return num + sum(num + 1);
}
//删除某一个目录下面的所有文件
public static void main(String[] args) {
File file = new File();
del(file);
}
public static void del(File file){
//找到bbb文件夹下面所有的文件和文件夹
File[] files = file.listFiles();
for(File file1 : filse){
if(file1.isDirectory()){
del(file1);
}else{
file1.delete();
}
}
}
四.io流
1.io流概念
以后会遇到 上传和下载 等这些需求。
I input 输入
O output 输出
咱们电脑上面文件,在进行读取和存储的时候,都是以流的形式进行操作的
流这个概念是比较抽象的
2.缓冲的概念
看视频有点卡? 暂停的时候在缓冲的
缓冲其实就是为了提高读取和存储的效率的
计算机通过cpu读取硬盘的数据,在Java中可以加上缓冲的概念,每次读取具体的缓冲值。可以提高效率
3.IO流
从磁盘(c盘)读取数据到内存(Java代码) 1.txt====》java代码(输出出来)
输入流:
input :场景使用 从磁盘的c:/aaa/1.txt文件中 读取数据 到内存中(Java代码)
字节输入流
字符输入流
从内存(Java代码 String=“狗蛋”)写入数据到 磁盘(c盘 1.txt)
输出流:
output : 从内存(String str = " 还是数据库的") 写入到磁盘的c:/aaa/1.txt文件中
字节输出流:
字符输出流
参照物体 是内存
3.1字节输入流
FileInputStream
作用:将磁盘上面文件的内容读取到java代码中
public static void mian(String[] args) throwe IOException {
//1.创建一个file对象的 文件抽象
File file = new File(D:/aaa/1.txt);
//2.创建字节输入流的核心的类
FileInputStream fis = new FileInputStream(file);
//3.加缓冲功能
BufferedInputStream bis = new BUfferedInputStream(fis);
//4.创建一个数组 缓冲数组
baby[] buf = new baby[1024];
int length;
while((length = bis.read(buf)) != -1){
System.out.println(new String(buf,0,length));
}
bis.close();
fis.close();
}
简写:
BufferedInpubStream bis = new BufferedInpubStream(new FileInputStream(new File(D:/aaa/1.txt)))
3.2字节输出流
FileOutpubStream
将Java代码中的一个字符串 写入到磁盘中的一个1.txt
public static void mian(String[] args) throwe IOException {
//1.创建文件对象
File file = new File("c:/aaa/2.txt");
//2.创建核心的字节输出流对象FileOutputStream
FileOutputStream fos = new FileOutputStream(file);
//3.加缓冲的效果 BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(fos);
//4.声明一个字符串
String str = "abcdefg";
bos.write(str,0,str.length);
bos.close();
fos.close();
}
简写:
BufferedOutputStream bos = new BufferedOutputSTream("D:/aaa/1.txt");
针对于上面讲的写一个案例:
c:/bbb下面有一个视频文件 复制到c:/aaa下面
使用Java代码
思路: 先从磁盘读取到内存缓冲数组(输入流)中然后再写入磁盘中(输出)
public static void main(String[] args) throws IOException {
copyVideo1();
}
public static void copyVideo () throws IOException {
//创建缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("c:/bbb/12转义字符.mp4")));
//创建缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("c:/aaa/sb.mp4")));
//准备一个缓冲数组
byte[] buf = new byte[4096];
int length;
while ((length = bis.read(buf)) != -1) {//length = bis.read(buf) 从磁盘读取数据到缓冲数组中
bos.write(buf, 0, length);//从缓冲数组中写入到磁盘
}
bos.close();
bis.close();
}
//不带缓冲的
public static void copyVideo1 () throws IOException {
FileInputStream fis = new FileInputStream(new File("c:/bbb/12转义字符.mp4"));
FileOutputStream fos = new FileOutputStream(new File("c:/aaa/2b.mp4"));
int length;
while ((length = fis.read()) != -1) {
fos.write(length);
}
fos.close();
fis.close();
}
}
3.3字符输入流
FileReader
将磁盘中的文件的内容读取到Java代码中
阅读字符文件的便利类
FileReader
是用于读取字符流。 要读取原始字节流(图片 音频 视频),请考虑使用FileInputStream
FileReader是从字节流到字符流的桥:它读取字节,并使用指定的
charset
将其解码为字符 。
public static void main(String[] args) throws IOException {
//1.创建file对象
File file = new File("c:/aaa/1.txt");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
char[] cbuf = new char[2];
int length;
while((length = br.read(cbuf)) != -1){
System.out.println(new String(cbuf,0,length));
}
br.close();
fr.close();
}
3.4字符输入流
FileWriter
场景: Java中有一个字符串然后写入到磁盘的一个文件中
public static void main(String[] args) throws IOException {
File file = new File("c:/aaa/1.txt");
FileWriter fw = new FileWriter(file,true);
BufferedWriter bw = new BufferedWriter(fw);
String str = "abcdefg";
bw.write(str);
bw.close();
fw.close();
}
复制一个文本文档到另外一个盘符下面
public static void main(String[] args) throws IOException {
//字符流将一个文本赋值2到另外一个文件夹下面
//BufferedReader 专门将文本内容读取到内存中
BufferedReader br = new BufferedReader(new FileReader("c:/bbb/《雪中悍刀行》.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("c:/aaa/88.txt"));
char[] cbuf= new char[1024];
int length;
while ((length = br.read(cbuf)) != -1) {
bw.write(cbuf, 0, length);
}
bw.close();
br.close();
}
总结:
1.输入流和输出流的功能
输入流: 从磁盘到内存(Java代码)
输出流: 从内存(java 代码)到磁盘
2.输入流
分为两种:
字节输入流
FileInputSrteam
字符输入流
FileReader
核心方法: read
关于效率问题,他们两个有对应的缓冲流
FileInputSrteam 对应的BufferedInputStream
FileReader 对应的 BufferedReader
3.输出流
分为两种:
字节输出流:
FileOutputStream
字符输出流:
FileWriter
核心的方法 write
关于效率问题,他们两个有对应的缓冲流
FileOutputStream对应的缓冲流 BufferedOutputStream
FileWriter对应缓冲流 BufferedWriter
4.记住一句话 用字节流
五.序列化和反序列化
新建类然后创建对象,然后对对象的属性赋值。对象真实存在了,然后可以通过流来将对象存到本地磁盘上面,这就叫序列化。 本次磁盘上面存的有对象的数据,咱们可以再通过流读取到Java代码中变成对象
这叫反序列化 持久性操作
从内存到磁盘 序列化 输出流 ObjectOutputStream write
class Employee implements Serializable{
String name;
int age;
transient int ID;//短暂的 此属性不可序列化
String adress;//地址
public void eat(){
System.out.println("今天中午很开心");
}
}
public class Xuliehua {
public static void main(String[] args) throws IOException {
ArrayList arrayList = new ArrayList();
Employee employee = new Employee();
employee.name = "gousheng";
employee.adress = "航海中路";
employee.age = 18;
employee.ID = 8989;
File file = new File("D:/aaa/1.ser");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(employee);
oos.close();
fos.close();
}
}
class Employee implements Serializable{
String name;
int age;
transient int ID;//短暂的 此属性不可序列化
String adress;//地址
public void eat(){
System.out.println("今天中午很开心");
}
}
public class Xuliehua {
public static void main(String[] args) throws IOException {
ArrayList arrayList = new ArrayList();
Employee employee = new Employee();
employee.name = "gousheng";
employee.adress = "航海中路";
employee.age = 18;
employee.ID = 8989;
File file = new File("D:/aaa/1.ser");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(employee);
oos.close();
fos.close();
}
}
反序列化 从磁盘到 内存中 inputStream
public class Fanxuliehua {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("D:/aaa/1.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Employee emp =(Employee)ois.readObject();
System.out.println(emp.name);
System.out.println(emp.age);
System.out.println(emp.adress);
System.out.println(emp.ID);//0
}
}
注意事项:
请注意,一个类的对象要想序列化成功,必须满足两个条件:
该类必须实现 java.io.Serializable 接口。
该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的
六.常用类
1.StringBuffer
描述:
线程安全,可变的字符序列。 字符串缓冲区就像一个String,但可以修改。 在任何时间点,它包含一些特定的字符序列,但可以通过某些方法调用来更改序列的长度和内容。
构造方法:
StringBuffer sb = new StringBuffer("abc");"abc“ 0x007
sb.append("ef"); 字符串变成 "abcef" 0x007
方法:
增:
append("a"); //追加
insert(2,"b") //指定位置添加
delete(2,4) //从指定位置删除
package com.qf.a_stringbuffer;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo1 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("abcdef");
System.out.println(sb);
//System.out.println(sb.toString());//将StringBuffer类型的数据转为String
sb.append("a");//追加 abcdtrue
sb.insert(2, "狗");
//ab狗cdefa
sb.delete(2, 4);//abdefa
//字符串反转
System.out.println(sb.reverse());//afedba
System.out.println(sb);
System.out.println(sb.capacity());//容量 容量是新插入字符可用的存储量
StringBuffer sb1 = new StringBuffer();
System.out.println(sb1.capacity());//16
sb1.append("12345678912345671");
System.out.println(sb1.capacity());
}
}
2.枚举类
Java中有一个特殊的类叫枚举类,一般表示的是常量。
public static final int A = 23;
枚举就是替换上面常量的写法的!!!!
语法格式:
public enum 枚举类名 {
各组常量,常量之间使用逗号隔开
}
package com.qf.b_enum;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
enum Color {
RED, GREEN, BLUE
}
public class Demo1 {
public static void main(String[] args) {
Color red = Color.RED;
System.out.println(red);//RED
}
}
构造方法
package com.qf.b_enum;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
enum Ball {
TYPE_BALL_FOOTBALL(1, "足球"),
TYPE_BALL_PINGPANGBALL(2, "乒乓球"),
TYPE_BALL_BASEGETBALL(3, "篮球"),
;
int key;
String name;
Ball(int key, String name) {
this.key = key;
this.name = name;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Demo3 {
public static void main(String[] args) {
Ball.TYPE_BALL_FOOTBALL.setKey(4);
int key = Ball.TYPE_BALL_FOOTBALL.getKey();
System.out.println(key);
String name = Ball.TYPE_BALL_FOOTBALL.getName();
System.out.println(name);
}
}
枚举方法
values(); 枚举类中的所有的值
oridnal();每个常量的索引值
valueOf();返回值的指定的字符串的常量
package com.qf.b_enum;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
enum Color1 {
RED, GREEN, BLUE
}
public class Demo4 {
public static void main(String[] args) {
Color1[] values = Color1.values();
for (Color1 value : values) {
System.out.println(value +"对应的索引:" + value.ordinal() );
}
//valueOf();返回值的指定的字符串的常量
Color1 red = Color1.valueOf("RED");
System.out.println(red);
}
}
Java在生产环境中如何使用的
枚举一般在使用的时候确定业务场景的。
支付功能,需要使用以下几种支付的方式:
微信小程序支付 wxma
微信H5支付 wxh5
支付宝小程序支付 zfbma
支付宝生活号支付 zfbshm
微信医保支付 wxyb
可以使用枚举类来维护这几种支付方式,一旦需要新增,需要修改 需要删除的时候,随时修改枚举类即可
package com.qf.b_enum;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
//支付方式的枚举类
enum PayTypeEnum {
WEI_XIN_MINI_APP("1", "wxma", "微信小程序支付"),
WEI_XIN_H5("2", "wxh5", "微信H5支付"),
ZFB_MINI_APP("3", "zfbma", "支付宝小程序支付"),
ZFB_SHH("4", "zfbshm", "支付宝生活号支付"),
WEI_XIN_YB("5", "wxyb", "微信医保支付"),
;
private final String id;
private final String code;//支付类型码
private final String type;//支付方式类型
PayTypeEnum(String id, String code, String type) {
this.id = id;
this.code = code;
this.type = type;
}
public String getId() {
return id;
}
public String getCode() {
return code;
}
public String getType() {
return type;
}
}
public class Demo5 {
public static void main(String[] args) {
//获取枚举类的id‘值
String id = PayTypeEnum.ZFB_MINI_APP.getId();
System.out.println(id);
}
}
package com.qf.b_enum;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
enum SexEnum {
MALE(0, "男"),
FEMALE(1, "女"),
;
private int sex;//0代表男 or 1 代表女
private String sexName;//男 or 女
SexEnum(int sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
public int getSex() {
return sex;
}
public String getSexName() {
return sexName;
}
}
class User {//用户类
private String name;//用户名字
private SexEnum sex;//用户的性别
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SexEnum getSex() {
return sex;
}
public void setSex(SexEnum sex) {
this.sex = sex;
}
}
public class Demo6 {
public static void main(String[] args) {
User user = new User();
user.setName("狗蛋");//赋值
user.setSex(SexEnum.MALE);
System.out.println(user.getSex().getSexName());
}
}
3.包装类
Java八大基本数据类型,都有与之对应的包装类
为啥会有这些包装类?其实就代表基本数据类型所有东西
包装类能够实例化处理对象,有很多的方法,来处理当前的数据
这样来操作八大基本数据类型很方便
int==>Integer
byte===>Byte
short====>Short
long====>Long
float====>Float
double====>Double
boolean====>Boolean
char====>Character
【重点】:
1.自从jdk5之后 ,有自动拆箱和自动装箱
自动装箱: 将基本数据类型转为包装类类型
自动拆箱: 将包装类转为基本数据类型
2. static String toString(); 将基本数据类型转为 字符串 3. static parse***(); 将一个字符串转为 所对应基本数据类型
package com.qf.c_baozhuang;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
class User {
String name;
Integer age;
String email;
Integer sex;
}
public class Demo1 {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
//1.自动装箱: 将基本数据类型转为包装类型
int i = 30;
Integer i1 = i;
System.out.println(i1.hashCode());
//2.自动拆箱: 将包装类转为基本数据类型
int i2 = i1;
System.out.println(i2);
//3.***Value();
Integer i3 = 40;//i3是包装类类型的数据
int i4 = i3.intValue();//intValue
System.out.println(i4);//i4是基本数据类型
//shortValue()
//4.toString方法
String s = Integer.toString(34);
System.out.println(s);
//5将整数字符串转为基本数据类型
int i5 = Integer.parseInt("999");
System.out.println(i5);//999
double v = Double.parseDouble("89.7");
//
Integer i6 = 30;
Integer i7 = 30;
System.out.println(i6 == i7);//true
Integer i8 = new Integer(30);
System.out.println(i6 == i8);
Integer i9 = 129;
Integer i10 = 129;
System.out.println(i9 == i10);//false
}
}
有兴趣的话,看看Integer的底层
4.Math
Math
类包含执行基本数字运算的方法,如基本指数,对数,平方根和三角函数。
package com.qf.d_math;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo1 {
public static void main(String[] args) {
System.out.println(Math.E);
System.out.println(Math.PI);
System.out.println(Math.abs(-89));//求一个数的绝对值 absolute
System.out.println(Math.max(3, 7));//7 求两个数最大值的
System.out.println(Math.max(1, Math.max(3, 9)));//9
System.out.println(Math.min(1, 2));
System.out.println(Math.ceil(34.5));//向上取整
System.out.println(Math.floor(34.5));//34.0 向下取整
System.out.println(Math.round(34.6));//35 long
System.out.println(Math.random());//double 大于等于 0.0 ,小于 1.0 。
}
}
5.Random
package com.qf.e_random;
import java.util.Random;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo1 {
public static void main(String[] args) {
Random random = new Random();
System.out.println(random.nextInt());
System.out.println(random.nextBoolean());
System.out.println(random.nextInt(3));
System.out.println(random.nextDouble());
}
}
6.System
System
类提供的System
包括标准输入,标准输出和错误输出流; 访问外部定义的属性和环境变量; 一种加载文件和库的方法; 以及用于快速复制阵列的一部分的实用方法。
package com.qf.f_system;
import java.io.PrintStream;
import java.util.Properties;
import java.util.Scanner;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo1 {
public static void main(String[] args) {
PrintStream out = System.out;//标准输出
out.println("hehe");
System.out.println("xiix");
Scanner scanner = new Scanner(System.in);
//m err
//“标准”错误输出流。
System.err.println("haha");
//返回当前的时间
long l = System.currentTimeMillis(); //ms
System.out.println(l);//1680076586166
//在1970年1月1日UTC之间的当前时间和午夜之间的差异,以毫秒为单位。
//通过类 获取当前系统的一个属性
Properties properties = System.getProperties();
System.out.println(properties.get("os.name"));//Windows 10
System.out.println(properties.get("user.name"));//bowang
System.out.println(properties.get("java.version"));//1.8.0_241
}
}
7.Date
专门处理时间的一个类
package com.qf.g_date;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo1 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
//Wed Mar 29 16:17:57 IRKT 2023
//我难受 看不懂? 吧咋办? 转成你能看懂的 格式化
//SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期
//SimpleDateFormat(String pattern)
//使用给定模式 SimpleDateFormat并使用默认的 FORMAT语言环境的默认日期格式符号。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sdf.format(date);
System.out.println(format);
}
}
package com.qf.g_date;
import java.util.Calendar;
import java.util.Date;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
public class Demo2 {
public static void main(String[] args) {
Date date = new Date();
//返回一个值,该值是从包含本开始时间的年份中减去1900的值
System.out.println(date.getYear() + 1900);
//返回一个数字,表示包含或开始于此Date对象所代表的时刻的月份 。 返回的值在0和11之间,其值为0代表一月。
System.out.println(date.getMonth() + 1);//2
//返回由此日期表示的星期几。 返回值( 0 =星期日, 1 =星期一, 2 =星期二, 3 =星期三, 4 =星期四, 5 =星期五, 6 =星期六)表示包含或以此时间表示的时刻开始的星期几Date对
System.out.println(date.getDay());//3
Calendar rightNow = Calendar.getInstance();//获取日历的对象
System.out.println(rightNow);
System.out.println(rightNow.get(Calendar.YEAR));//2023
System.out.println(rightNow.get(Calendar.MONTH) + 1);//3
System.out.println(rightNow.get(Calendar.DAY_OF_MONTH));//29
System.out.println(rightNow.get(Calendar.DAY_OF_YEAR));//88
System.out.println(rightNow.get(Calendar.DAY_OF_WEEK) - 1);//4
System.out.println(rightNow.get(Calendar.HOUR));//4 pm
System.out.println(rightNow.get(Calendar.HOUR_OF_DAY));//16
}
}
两个类:
BigDecimal
LocalDate
七.进程 线程
1.进程线程并行并发概念
进程概念:
1.独立性
每个进程之间是相互独立的,互不影响的.
2.互斥性
每个应用程序(软件)系统分配一个独立的端口号. 不能重复
线程概念:
进程是由至少一个或着多个线程组成的.
线程是进程最小的基本单位.
特性:
1.抢占式运行
给程序分配CPU, 按照时间片来执行, 单位时间片抢占式执行,随机抢占.
2.资源共享
同一个进程, 有多个线程, 这个多线程是可以共享同一个数据的.
并行和并发
并行:真正的同时执行
并发:同时发生,轮流交替执行
2.创建线程的两种方式
Java虚拟机允许应用程序同时执行多个执行线程。
方法1:
创建一个新的执行线程有两种方法。 一个是将一个类声明为`Thread`的子类。 这个子类应该重写`run`方法`Thread` 。 然后可以分配并启动子类的实例。
1.声明一个类继承Thread
2.重写run方法
3.新建线程的实例
4.start()启动线程
class A extends Thread{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println("A线程:"+i);
}
}
}
class B extends Thread{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println("B线程:"+i);
}
}
}
public class Xiancheng {
public static void main(String[] args) {
new A().start();
new B().start();
for (int i = 0; i < 101; i++) {
System.out.println("主线程:"+i);
}
}
}
方法2:
另一种方法来创建一个线程是声明实现类`Runnable`接口。 那个类然后实现了`run`方法。 然后可以分配类的实例,在创建`Thread`时作为参数传递,并启动。
1.声明一个类实现Runnable
2.重写run方法
3.新建线程的实例
4.传参给新建Thread实例
5.start()启动线程
class C implements Runnable{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println("C线程:"+i);
}
}
}
class D implements Runnable{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println("D线程:"+i);
}
}
}
public class Xiancheng2 {
public static void main(String[] args) {
C c = new C();
new Thread(c).start();
new Thread(new D()).start();
for (int i = 0; i < 101; i++) {
System.out.println("主线程:"+i);
}
}
}
3.Thread线程方法
Thread构造方法:
方法1无参构造:
Thread t = new Thread();
方法2分配一个新的Thread对象:
Thread t = new Thread(new 类名());
方法3分配一个新的Thread对象. 并对这个线程起一个名字
Thread t = new Thread(new 类名(),"线程名字")
Thread方法:
.currentThread()//返回当前正在执行的线程对象的引用
.getName()//返回此线程的名称
.setName(String name)//将此线程的名称更改为参数name
.getPriority()//返回此线程的优先级
.setPriority()//更改此线程的优先级
Thread.sleep(long millis)//使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。需要try,catch
4.线程同步和锁
当多个线程同时请求一个数据的时候,会导致数据不准确的情况。相互之间产生问题。容易出现线程安全的问题。比如多个线程操作同一个数据,都打算修改商品库存。
线程的同步真实的意思: 让你"排队",几个线程之间要排队。一个一个对共享资源进行操作,等一个结束以后,另外一个再进来操作。变量 是唯一的和准确的
可以加锁
synchronized 隐式锁,会自动释放,一个非公平的锁。
方法1:
同步方法:
public synchronized void run(){
}
只能有一个线程进入到方法中,其他线程在方法的外面等待
弊:太极端,一个线程执行完.
方法2:
同步代码块: 将一段代码放到synchronized 然后括起来。就会对这段代码加上锁。
sychronized(this){
}
class E implements Runnable{
private int length = 100;
@Override
public void run() {
while(true){
synchronized (this){
if (length > 0){ System.out.println(Thread.currentThread().getName()+ "卖出了第" + length-- +"票");
}else {
System.out.println("售空");
break;
}
}
}
}
}
public class Shuo1 {
public static void main(String[] args) {
E e = new E();
new Thread(e,"窗口1").start();
new Thread(e,"窗口2").start();
}
}
要求不能使用同步代码块来解锁,必须使用同步方法来加锁 来实现上面的案例
class E implements Runnable{
private int length = 100;
@Override
public void run() {
while(true){
test();
if (length <= 0){
break;
}
}
}
public synchronized void test(){
if (length > 0){ System.out.println(Thread.currentThread().getName()+ "卖出了第" + length-- +"票");
}else {
System.out.println("售空");
return;
}
}
}
public class Shuo1 {
public static void main(String[] args) {
E e = new E();
new Thread(e,"窗口1").start();
new Thread(e,"窗口2").start();
}
}
5.Java中的锁
synchronized 被成为隐式锁,会自动释放,式一个非公平的锁。
Lock锁 被成为显示锁。
他们两个锁都可以解决线程同步的问题。但是synchronized 更加强大,更加粒度化。更加灵活。
所以一般开发时候用synchronized 。以后还会有线程池,也有锁 更高级。
Lock是一个接口,实现ReentrantLock
有两个重要方法:
lock();
unlock();
不安全
class F implements Runnable{
private int ticket = 100;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
lock.lock();
if (ticket > 0){
System.out.println(Thread.currentThread().getName()+ "卖出了第" + ticket-- +"票");
}else{
System.out.println("售空");
break;
}
}catch (Exception e) {
} finally {
lock.unlock();
}
}
}
}
public class Shuo2 {
public static void main(String[] args) {
new Thread(new F(),"线程1").start();
new Thread(new F(),"线程2").start();
}
}
6.守护线程【了解】
守护线程是用来守护非守护线程的
class E implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("守护线程:" + i);
}
}
}
public class Demo3 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.isDaemon());//false 非守护线程
// thread.setDaemon(true);//设置为守护线程
Thread thread1 = new Thread(new E());
//System.out.println(thread1.isDaemon());
thread1.setDaemon(true);
thread1.start();
for (int i = 0; i < 200; i++) {
System.out.println("主线程:" + i);
}
}
}
八.死锁
1.死锁
开发中禁止出现死锁
面试会问:
应用场景: 并发场景,多个线程。线程之间在共享数据的时候 是互不相让的
线程加锁为了线程安全,但是物极必反。
死锁是一种状态,当两个线程互相持有对象所需要的资源的时候,这两个线程又都不主动释放资源
就会导致死锁。代码无法正常执行。这两个线程就会僵持住。
package com.qf.a_sisuo;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
class DeadLock implements Runnable {
private boolean flag;//标记
private Object obj1;//对象1
private Object obj2;//对象2
public DeadLock(boolean flag, Object obj1, Object obj2) {
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
if (flag) {//如果flag = true 让线程1执行这个if语句里面的代码
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "拿到了obj1资源");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1执行了");
synchronized (obj2) {//想用obj2这个资源
System.out.println(Thread.currentThread().getName() + "拿到obj2资源");
}
}
}
if (!flag) {//如果flag=false 线程2 执行这个if语句里面的代码
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "拿到了obj2资源");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2执行了");
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "拿到obj1资源");
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
DeadLock deadLock1 = new DeadLock(true, obj1, obj2);
new Thread(deadLock1, "线程1").start();
DeadLock deadLock2 = new DeadLock(false, obj1, obj2);
new Thread(deadLock2, "线程1").start();
}
}
2.Object类下面的和线程相关的方法
public final void wait() throws InterruptedException
导致当前线程等待,直到另一个线程调用该对象的
notify()
方法或notifyAll()
方法。总结: 至少两个线程, 对象.wait() ,那么当前线程就会等待。
wait1线程正在等待中....
唤醒线程已经执行
wait1线程被唤醒!!!Line45行的
我是修改之后的message对象
wait2线程正在等待中....
package com.qf.b_object;
/**
* description:
* 公司:千锋教育
* author:博哥
* 公众号:Java架构栈
*/
//为啥写Message这个类? wait方法 对象.wait(); 创建Message对象
//
class Message {
private String message;//信息
public Message(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
//线程类 等待线程
class WaitThread implements Runnable {
private Message message;
public WaitThread(Message message) {
this.message = message;
}
//等待线程抢到了
//等待线程睡了5秒 然后唤醒线程执行。 synchronized (message)
//message 对象从等待池中国去取的,结果发现没有 阻塞
//回到等待线程睡醒了以后开始 wait等待
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "正在等待中....");
synchronized (message) {
try {
//当调用wait方法的时候,会自动释放锁,并将对象放到等待池中,让唤醒线程锁来操作这个对象
//
message.wait();//代码走到这一步 当前线程会等待!!!
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name +"被唤醒!!!Line45行的");
System.out.println(message.getMessage());
}
}
}
//唤醒线程
class NotifyThread implements Runnable {
private Message message;
public NotifyThread(Message message ) {
this.message = message;
}
@Override public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒线程已经执行");
synchronized (message) {
message.setMessage("我是修改之后的message对象");
//message.notify();
message.notifyAll();
}
}
}
public class Demo1 {
public static void main(String[] args) {
Message message = new Message("我是message对象");
WaitThread waitThread = new WaitThread(message);
WaitThread waitThread1 = new WaitThread(message);
NotifyThread notifyThread = new NotifyThread(message);
new Thread(waitThread1, "wait2线程").start();
new Thread(waitThread, "wait1线程").start();
new Thread(notifyThread, "notify线程").start();
}
}