Java基础
注意:长篇警告!!目前本笔记7w+字,且图片没能上传成功(懒得搞了)如果觉得有用可以私信我。本笔记为个人学习过程中记录,所学课程均来自网络公开课,笔记内容如有错误欢迎指正。本笔记仅供个人学习使用,转载请联系私信本人
前言:
- 不同版本
javaSE 桌面
javaME 移动
javaEE 服务器
- 跨平台
jvm使得java可以跨平台,我们编写的java程序都是编程jvm字节码在jvm上执行,不同的操作系统负责实现本操作系统下的jvm
- 特性
简单,面向对象,可移植性,高性能,分布式,动态性,多线程,安全性,健壮性
JDK Java Development Kit 包括jre和jvm还有一些他们没有的东西
JRE Java Runtime Environment 除了jvm还有一些其他运行需要的东西(只需要运行java程序只需要安装jre即可)
jvm Hava Virtual Machine
- java程序运行机制
源文件首先编译成字节码(.class)之后再通过类加载器,字节码校验器 解释器才能运行
一个java文件中只有一个public类,可以有多个class
public class Outer{
}
class test{
public static void main(String[] args){
//这里写Outer类的测试
}
}
文章目录
- Java基础
- IDEA快捷键
- 第一章 (基础)
- 第二章(常用类,集合框架,Io)
- 第三章 集合框架
- 第四章 文件和IO流
- 第五章 多线程&JUC
- 第六章 注解(Annotation)和反射
- 第七章 JVM
- 第七章 JVM
IDEA快捷键
psvm //自动生成main函数
sout //自动生成输出
重写方法 alt+insert
选中代码 CTRL+alt+t 抛出异常
alt+7 列出来类的方法大纲
ctrl+f12 类的结构
第一章 (基础)
1.1基本数据类型/变量
- 基本数据类型 (和其他语言雷士)
- byte 1字节
- short 2 byte
- int 4byte
- long 8byte
- float 4byte
- doublt 8byte
- char 2byte
- boolean 1bit
- 引用数据类型 (类 接口 数组)
1.1.1类型定义
// 【1】 基本数据类型
int num=0;
short num3=30;
long num4=20L; //add L
float num5=123.3F; //add F
double num6=1.123;
char name='A';
String name="啊士大夫看见"; //String is not a key word,but a class
Integer name =123124; //Integer is not a key word,but a class
// 【2】 进制 二进制0b 八进制0 十六进制0x
int i=10;
int i2=010;
int i3=0x10l;
// 【3】 浮点数 舍入误差,最好不要用浮点数进行比较
// 【4】 字符扩展 Unicode 2字节编码 0-65536 97-a U0000-UFFFF
char cs ='\u0061'; //unicode编码
// 【5】 String类
String a=new String("hello");
String b = new String("hello"); //a!=b
String c="hello";
String d="hello"; //c==d
1.1.2类型转换
// 【1】 强制类型转换的时候避免内存溢出 (高到低)
int i=128;
byte b=(int)i;
// 【2】 自动转换 (低到高)
int i =123;
double b=i;
// 【3】 计算之前如果有溢出,可以先转换再计算
long total = 1000000000*(long)20;
1.1.3变量 常量 作用域
final 表示常量
static 类变量可以在类的方法中直接使用
修饰符不存在先后顺序
1.2运算符
// 【1】 Math类,java中没有^幂运算,需要用到Math类的pow方法
// 【2】 条件运算符 ?:
int a = x?a:b; //如果x为真a=a,否则a=b
1.3用户交互
// 【1】 接受用户输入
Scanner scanner = new Scanner(System.in);
//判断用户是否输入
if(scanner.hasNext()){
String str= scanner.next(); //空格隔开 nextLine可以接受一行以为回车为结束符
System.out.println(str);
}
scanner.close();
1.4程序结构
//# 1 switch
switch(expression){
case value:
//
break;
case value:
//
break;
default:
//
}
//# 2 增强for循环
for(int x:numbers){
//numbers是可遍历数据类型,
}
1.5方法
java是值传递,不是引用传递
1.5.1可变参数
// 【1】 只可以指定一个可变参数,必须在普通参数之后
public void test(int... i){ //【可变长的参数,封装到一个对象,本质就是一个数组,通过数组下表来找到不同的值】
System.out.println(i[0]);
}
1.6数组
数组的长度在创建后是不变的
数组是一个对象
// 【1】 数组的属性
num.length
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r0hIPok6-1681980768116)(…/Asset/image-20230413163002023-16816270541451.png)]
1.6.1数组的声名和创建
int[] nums; //定义
nums=new int[10]; //使用数组,可以存放10个int类型数字
1.6.2三种初始化
// 【1】 静态初始化
int[] a={1,2,3,4,5}
Man[] manx={new Man(1),new Man(2)}
// 【2】 动态初始化
int[] nums=new int[10]; //创建数组后,通过下标来赋值
nums[0]=1;
// 【3】 默认初始化
int[] nums=new int[10]; //这里面有默认的值0
1.6.3数组工具类Arrays
// 【1】 打印数组
int[] a={1,2,3,4,5};
Arrays.toString(a);
// 【2】 数组填充
int[] a={1,2,3,4,5}
Arrays.fill(a,0); //[0,0,0,0,0]
1.7面向对象
栈是一些方法和引用
堆里面才是实际的对象,其中堆里面有个特殊的方法区,存放的是类的方法,类加载的时候加载到这里,同时一些静态方法也在这里,随这类一块加载。
类的具体对象放在堆中,对象属性修改,实际上是把创建该对象的类里面的一些值赋值给该对象。
一个类只有一个Class对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aUDt7Ilc-1681980768117)(…/Asset/image-20230413165349580-16816270566443.png)]
1.7.1继承
idea中ctrl+h查看当前类的继承关系
java中只有单继承,没有多继承
java中final修饰的类无法被继承
- this指代自己,super指代直接继承的父类方法,super只能出现在子类的方法中
- 私有无法继承
- 调用父类的构造器,调用自己的构造器,只能放在第一行
- 继承的无参构造会自动调用父类的无参构造
- 有参构造写好后,自动销毁无参构造,并且该类的子类也同样无法写无参构造。因此写了有参构造后一定要手动添加无参构造方法
- this()本地无参构造 super()父类无参构造
重写方法
- 方法名相同
- 参数列表必须相同
- 修饰符,范围可以扩大但不能缩小
- 抛出异常,范围可以缩小,不能扩大(父类没有抛出异常,子类就不可以抛出异常)
//【1】方法重写(静态方法) 和重载不太一样,和属性无关
A a=new A();
a.test(); //子类的方法
B b=new A(); //父类的引用指向子类,
b.test(); //父类的方法【方法的调用只和该引用的类型有关,引用
//【2】方法重写(非静态方法) 谁的实例调谁的方法
1.7.2多态
动态编译,能执行的方法看引用的类型,对象的实例时确定的,时右面new的类型
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系,否则类形转换异常
- 继承关系,方法重写,到底执行谁只有在运行的过程中才能判定(因为可以类型转换)
1.7.3 instanceof 和类型转换
- instanceof
instanceof 判断一个对象时什么类型
x instanceof y //x是y的子类,结果是true
Object object = new Student();
object instanceof Student;//true
object instanceof Person;//true
object instanceof Teacher;//false
- 类型转换
子类转换为父类可能会丢失一些方法。
父类转换为子类,强制转换
1.7.4抽象类和接口
- 抽象类
- 有抽象方法的类就是抽象类,抽象方法用abstract修饰。类里面也可以有普通方法
- 不能new,只能靠子类来实现,new子类…可以看作是个框架。
- 抽象类还是各类,只能用extends来继承,只能单继承
public abstract class Action{
public void run(){}
}
public class example extends Action{
public void run(){
System.out.println("run");
}
}
- 接口
- 只有规范,没有普通方法,实现约束和实现分离
- 不用class用interface
- 接口不能被实例化,接口中没有构造方法
public interface UserService{
//默认都是public abstract
public abstract void run();
void run();//和上面的定义相同
void add(String str);
}
public class UserServiceImpl implements UserService{
//这里面需要重写接口的所有方法
}
1.7.5内部类
- 成员内部类
public class Outer{
private int id;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
public void getId(){
System.out.println(id);//内部类可以操作外部类的私有属性
}
}
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
public void getId(){
System.out.println(id);//【这里会报错,因为类是static但是属性不是static属性】
}
}
}
Outer outer=new Outer();
Outer.Inner inner = outer.new Inner();// 通过外部类来new内部类
- 局部内部类,在方法中的类,只局限于方法内部
- 静态内部类
public class Test{
private name='123';
static class inner{
private age=20;
public void fun(){
sout(age,Test.name)
}
}
}
Test.inner = new Test.inner();//可以直接new
- 匿名内部类
interface UserService{
void hello();
}
public class Test{
public static void main(String[] args){
//下列下发就是匿名内部类的形式
new Uservice(){
public void hello(){
System.out.println("实现接口的方法");
}
}.hello();
//上述写法等价于
calss User implements UserService{
public void hello(){
System.out.println("实现接口的方法");
}
}
User user= new User();
user.hello();
}
}
1.8static关键字
-
static属性属于类,用类名访问,被所有对象共享
-
static方法属于类,类加载的时候就加载了,不需要实例,直接用类名来调用
-
static方法里面只能调用static修饰的方法
-
静态代码块,类加载额时候只执行一次
public class Person{
static{
System.out.println("静态代码块")//最先执行,只执行一次
}
{
System.out.println("匿名代码块")//第二执行,赋初始值,可以多次执行
}
public Person(){
System.out.println("构造方法")//最后执行
}
}
1.9异常
- 检查性异常:用户错误操作引起的异常
- 运行时异常
- 错误ERROR 大多数都是虚拟机抛出,Error后虚拟机会选择终止
异常处理框架:所有的异常当作一个对象处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhYM0ePc-1681980768118)(…/Asset/image-20230414150322748-16816270763905.png)]
异常处理:
- jvm处理及默认处理(打印异常信息到控制台,程序终止)
- 抛出异常程序处理
//【1】初识异常 选中代码块后 按ctrl+alt+t
public class Test {
public static void main(String[] args) {
int a=1;
int b=0;
try {//监控区域
System.out.println(a/b);
}catch (Exception e){//里面是需要捕获的异常类型
System.out.println("程序出现异常");
e.printStackTrace();//打印错误信息
}catch(Throwable t){
System.out.println("【==异常可以多次catch,注意小的异常要先写==】");
}finally {
System.out.println("善后工作");
}
try{
new Test().test(1,0);
}catch(Exception e){
e.printStackTrace();
}
}
public void test(int a,int b){
if(b==0){
throw new Exception();//抛出异常,一般在方法中使用,本方法不处理,交给调用改方法的角色去处理
}
System.out.println(a/b);
}
}
//【2】自定义异常
public class MyException extends Exception{
//传递数字,如果数字大于10抛出异常
private int detail;
public MyException(int a){
this.detail=a;
}
//异常打印信息
@Override
public String toString() {
return "MyException{" +
"detail=" + detail +
'}';
}
}
public class Test {
static void test(int a) throws MyException{
if(a>10){
throw new MyException(a); //抛出异常,不在这里处理
}
System.out.println("ok");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println("MyException=>"+e); //这里的e就是自定义异常的tostring方法的返回值
}
}
}
1.10Stream()流
先学完集合框架后再来看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWaVzvIc-1681980768118)(…/Asset/image-20230417155347508.png)]
使用步骤:
- 得到Stream流
- 利用中间方法过滤
- 利用终结方法输出
1.10.1 获取Stream流
注意:双列集合只能通过key获取set集合后,再利用单列集合来操作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7QOwW82v-1681980768118)(…/Asset/image-20230417155451487.png)]
// 【1】 单列集合获取Steam
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"123","231234","!312423","12344","1234","1234324","3253254");
Stream<String> stream1 = list.stream(); //获取集合的流水线,然后放到流水线上
// 【2】 双列集合获取Stream 根据key
HashMap<String, String> hm = new HashMap<>();
hm.put("123","321");
hm.put("231","432");
Stream<String> stream2 = hm.keySet().stream();
// 【3】 双列集合获取Stream 根据Entries
HashMap<String, String> hm = new HashMap<>();
hm.put("123","321");
hm.put("231","432");
hm.entrySet().stream().forEach(s-> System.out.println(s));
// 【4】 数组获取Stream 【利用arrays来获取】
int[] arr={1,2,3,4,5,6,7};
Arrays.stream(arr).forEach(s -> System.out.println(s)); //【无论是引用数据类型还是自定义数据类型的数组都可以用这个方法】
// 【5】 凌乱的一堆数据(同类型) 【利用Steam.of方法】
Stream.of(1,2,3,4,6).forEach(S-> System.out.println(S));//
1.10.2中间方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MmLlIHnP-1681980768118)(…/Asset/image-20230417160444234.png)]
// 【1】 过滤
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"123","231234","!312423","12344","1234","1234324","3253254");
//注意流只能使用依次,建议中间方法和终结方法利用链式编程
list.stream()
.filter(s -> s.startsWith("1"))
.forEach(s -> System.out.println(s));
// 【2】 limit获取前面3个元素 skip写法和limit类似
list.stream()
.limit(3)
.forEach(s -> System.out.println(s));
// 【3】 distinct 去重
list.stream()
.distinct()
.forEach(s -> System.out.println(s));
// 【4】 concat合并
Stream.concat(list1.stream(),list2.stream()).forEach(s->sout(s));
// 【5】 map类型转换 【String->int】
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
list.stream()
.map(s-> Integer.parseInt(s.split("-")[1]))
.forEach(s-> System.out.println(s));
1.10.3 终结方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8SYOV9K-1681980768119)(…/Asset/image-20230417162100746.png)]
// 【1】 收集数据 toArray()
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
//String表示要转化为String数组,value表示流里面的数据个数
String[] strr = list.stream()
.map(s -> s.split("-")[1])
.toArray(value -> new String[value]);
System.out.println(Arrays.toString(strr)); //【数组的输出用Arrays里的toString方法】
// 【2】 收集到集合中 collect
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
//String表示要转化为String数组,value表示流里面的数据个数
List<String> collect = list.stream()
.map(s -> s.split("-")[1])
.collect(Collectors.toList()); //【collector.toList()创建list集合,同理toSet会帮我们创建一个hashSet集合并把数据放到集合里面】
System.out.println(collect);
// 【3】收集到Map中 【注意:键不能重复】
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
Map<String, Integer> collect = list.stream()
//toMap中有两个参数,一个是键的规则,一个是值的规则
//Function<当前流的类型,最终键或值的类型>
.collect(Collectors.toMap(new Function<String, String>() {
@Override
public String apply(String s) {
return s.split("-")[0];
}
}, new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[1]);
}
}));
System.out.println(collect);
1.11方法引用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JqfH6fj5-1681980768119)(…/Asset/image-20230417165027396.png)]
// 【1】 快速入门方法引用
public static int subtraction(int num1,int num2){
return num2-num1;//num2大于num1 从大到小
}
Arrays.sort(arr,类名::方法名); //
1.11.1引用静态方法
类名::静态方法名字
list.stream().map(Integer::paraseInt).forEach();
//其中paraseInt就是把String转换为int
1.11.2引用成员方法
对象::方法名
this::方法名
super::方法名
public class StringOperation {
public boolean stringJudge(String s) {
return s.length()==3&&s.startsWith("张")
}
}
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
list.stream().filter(new StringOperation()::stringJudge).forEach();
1.11.3引用构造方法
类名::new
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三-3","李四-4","王五-23","天六-44","赵七-4","阿道夫-24","的萨芬-3254");
//注意map中的函数形参只有一个,因此Student的构造方法的形参也应该是一个。构造方法的返回值就是Student的对象
List<Student> students = list.stream().map(Student::new).collect(Collectors.toList());
System.out.println(students);
public class Student implements Comparable<Student>{
private int age;
private String name;
public Student(String s){
String[] strr = s.split("-");
this.name=strr[0];
this.age=Integer.parseInt(strr[1]);
}
}
1.11.4其他引用
- 使用类名::成员方法
引用规则:
- 函数式接口
- 被引用的方法必须存在
- 被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
- 被引用的方法可以满足要求
- 该引用方式下,抽象方法的第一个参数的类型决定了可以引用那些类(比如第一个参数是String,那么可以引用String的方法)
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"asdf","sadf","asdf","asdf","asdf","asdf","asdf");
list.stream().map(String::toUpperCase).forEach(s-> System.out.println(s));
}
- 引用数组的构造方法
数据类型[]::new
用该方法创建数组,需要使得流水线中数据类型和要创建的数组类型相同
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8);
Integer[] integers = list.stream().toArray(Integer[]::new);//【流里面的数据是Integer ,因此引用的是Integer数组的构造方法】
System.out.println(Arrays.toString(integers));
}
1.12 动态代理
不修改原码增加新的功能
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8llQNch5-1681980768119)(…/Asset/image-20230420144459472.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oGfNU3a4-1681980768119)(…/Asset/image-20230420144952139.png)]
- 创建代理对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NgHwD03n-1681980768120)(…/Asset/image-20230420145052082.png)]
// 【1】 原对象
public class BigStar implements star {
@Override
public String toString() {
return "BigStar{" +
"name='" + name + '\'' +
'}';
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
private String name;
@Override
public String sing(String name){
System.out.println(this.name+"正在长"+name);
return "谢谢";
}
@Override
public void dance(String name){
System.out.println(this.name+"正在跳舞"+name);
}
}
// 【2】 接口 是所有需要代理(增加新功能的代码)【代理和原类都要实现该方法】
public interface star {
//接口中写想要被代理的方法
public abstract String sing(String name);
public abstract void dance(String name);
}
// 【3】 代理
public class ProxyUtil {
//用于创建一个代理,形参是需要代理的对象 返回值是一个代理
public static star createProxy(BigStar bigStar){
/**
* 第一个参数 指定类加载器
* 第二个参数指定接口,接口用于指定生成的代理长什么,也就是有什么方法
* 第三个参数 指定生成的代理对象要做什么事情
*/
star s = (star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(), //找到该类的Class对象,获取加载该类的类加载器,用该加载器加载一次
new Class[]{star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 第一个参数:代理对象
* 第二个参数 要运行的方法
* 第三个参数 调用method方法是传入的实参
* */
//添加新的功能
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if ("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//执行原有的功能
return method.invoke(bigStar,args);
}
}
);
return s;
}
}
// 【4】 测试
class test{
public static void main(String[] args) {
//创建某个类的代理
star proxy = ProxyUtil.createProxy(new BigStar("ikun"));
proxy.dance("跳舞的名字");
proxy.sing("唱歌的名字");
}
}
第二章(常用类,集合框架,Io)
2.1Object类
方法名 | 作用 |
---|---|
对象名.equals(对象名) | 地址比较,如果需要值比较,需要在对象中重写equals方法 |
对象名.hashCode() | 为改对象生成一个独立的哈希码,用于区分每个对象,可以重写hashCode()让其和属性值相关联 |
对象名.toString() | 打印对象 |
2.1.1equals()
基本数据类型用== ==比较的是值,引用比较的是地址
equals比较的引用类的地址值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CJcFUXaM-1681980768120)(…/Asset/image-20230414154919652-16816270788567.png)]
//【1】重写equal,使得对象相比的时候比的是值
idea 会自动生成
2.1.2hashCode()
两个对象的值相同,这两个对象的hashCode一定相同(重写的结果)
2.1.2扩展 native关键字
native方法表示java调用调用非java代码的接口。
定义native方法时候
public int hashCode(){
return Objects.hash(name,age)//重写方法根据值计算hash值
}
2.1.3toString方法
输出实例对象的id可以认为是个地址值
可以重写该方法
Student stu=new Student();
sout(stu);//地址值
//重写toString
idea会自动重写
sout(stu);//这是一个学生
2.2包装类
把基本数据类型包装成对应的类
作用:
- 基本数据类型之间的转换
- 集合中放的是引用类型(非基本数据类型)
属性/方法 | 作用 |
---|---|
MAX_VALUE | 对应当前基本类型的最大值 |
TPYE | 对应当前基本数据类型 |
⭐Integer.parseInt(str) | 把String转换为int |
⭐Integer.valueof(str) | 把String类型转化为包装类 |
⭐对象名.toString | 转化为String类型 |
⭐对象名.intValue() | 把包装类变为基本数据类型 |
2.2.1自动装箱和拆箱
//【1】手动装箱拆箱
int a=10;
Integer integer=new Integer(a);
int i=integer.intValue();
//【2】自动
int b=10;
Integer integer2=b;//自动装箱
int c=integer2;//自动拆箱
//【3】隐藏装箱拆箱
Integer c=new Integer(10);
c++;//自动拆箱
int cc=10;
c.equals(cc);//隐藏装箱 把cc转换为包装类
2.2.2包装类常见问题
//【1】自动装箱拆箱注意问题
int a=10;
Integer b=10;
a==b;//b变为int,只是比的时候变为int【T】
b.equal(a);//a变为Integer 【T】
Integer a2=10;//自动装箱 【没有new新对象,返回的是缓存区中的地址(一个数)(-128,127)】【浮点类型没有,double类型没有 布尔类型没有】
Integer b2=10;//自动装箱 【没有new新对象,返回的是缓存区中的地址(一个数)(-128,127)】【浮点类型没有,double类型没有 布尔类型没有】
a2==b2;//还是值比较 【T】
a2.equal(b2);//自动装箱 值比较【T】
Integer a3=200;//自动装箱 【new新对象】
Integer b3=200;//自动装箱
a3==b3;//地址比较 【F】
a2.equal(b2);//自动装箱 值比较【T】
//【2】 每个包装类都有自己的缓冲区
2.3 String 类
- 底层JDK1.8
- char数组保存String
- String的长度就是char数组的长度
- concat是创建了一个新的char数组,返回的是一个新的String
- 底层JDK1.9
- 是byte数组。方便String的编码和重编码,节约空间(因为char默认占两个,byte占一个字节)
- 去长度的时候需要根据字符所占多少字节进行计算(如果全是英文,数组长度就是字符串长度,如果有中文,数组长度是字符串长度的二倍)
- concat和1.8类似,也是创建一个新的数组,只不过是byte数组
简单方法 | 功能 |
---|---|
str.length() | 字符串的长度 |
isEmpty() | 是否为空(检测内容是否为"") |
startsWith(“as”) | 以什么开始 |
endsWith(“as”) | 以什么结束 |
toUpperCase() | 算不变为大写 |
toLowerCase() | 全部变为小写 |
常用方法A | 功能 |
---|---|
str.charAt(int a)【return char】 | 取index为a的字符,下表从0开始 |
str.substring(0,2) | 从0-2,不包含2。 |
str.indexOf(string/int/char)【int】 | 返回字符(字符串)在字符串中首次出现的下标 |
str.indexOf(str,int) | 同上,int表示指定下标从哪里开始 |
str.lastIndexOf() | 同上 |
常用方法B | 功能 |
---|---|
str.getBytes()【byte[]】 | 吧string变为byte数组。以便后续进行重新编码 |
str.split(“-”)【string[]】 | 按照某个字符分割字符串 |
str.contains(CharSequences s)【Boolean】 | 判断字符串是否包含s |
str.replace(char oldChar,char newChar) | 用newChar替换oldChar |
str.replaceFirst(String oldChar,String newChar)【String】 | |
str.replaceAll(String oldChar,String newChar)【String】 |
常用方法C | 功能 |
---|---|
str.trim() | 去除首尾空格,可以去除多个 |
str1.compareTo(str2)【str1大返回+数,str2返回-数】 | 比较大小,依次按照字符的ASCII码比较。 |
str1.compareToIgnorCase(str2) | 同上,只不过忽略大小写 |
str1.concat(str2) | 产生新的对象(浪费内存) |
str1+str2 | 产生新的对象(浪费内存) |
2.3.1对象创建
String str="asdf";
String str=new String("asdf");
String ="";//有钱包美钱
String =null;//钱包都没有
// 【2】 byte数组创建字符串
String str=new String(bytes);
String str = new String(bytes,o,len);//把bytes数组从0开始长度为len切片,然后创建String数组
2.3.2StringBuffer和StringBuilder类
底层
- 如果创建的时候没有字符串,会自动创建一个长度为16的byte数组
- 如果有字符串,会创建一个字符串长度+16的byte数组(区分有没有中文,有中文的化创建字符串长度二倍+16的数组)。如果数组满了,再添加元素的时候,数组扩容为 添加前数组长度*2+2。如果扩容完都不够,直接把新计算后的长度作为新的长度
相同点:
- 均表示可变的字符序列
区别:
- buffer线程安全,做线程同步检查,效率较低
- stringBuilder 线程不安全,不做线程同步 效率高 建议采用该类
StringBuilder常用函数 | 功能 |
---|---|
builder.append() | 末尾追加 |
builder.insert(3,str) | str在当前位置追加 |
setChartAt(index,char) | 在index替换为新的char |
replace(start,end,str) | 替换吧[start,end)区间的字符串替换为str |
delete(start,end) | 删除指定区域的字符 |
reverse() | 反转字符串 |
toString() | 转成String |
// 【1】 StringBuilder的常用方法
StringBuilder builder="sadf"; //【X不能这么写】
StringBuilder builder=new StringBuilder("asdf");
builder.append();//最重要的函数,追加任何类型都可以追加
builder.insert(3,"123");
// 【2】 String转成StringBuilder
StringBuilder builder=new StringBuilder(str);
builder.toString();
2.4日期类
2.4.1Date类
**注意:**Date类已经过时了
方法(util中) | 功能 |
---|---|
data.toLocaleString() | 返回当前的年月日时分秒 |
data.getTime() | 获取当前系统毫秒数(统计代码的执行效率,作为唯一标识) |
System.currentTimeMillis() | 获取当前系统毫秒数(统计代码的执行效率,作为唯一标识(uuid也可以)) |
方法(sql中) | 功能 |
---|---|
data.valueOf(str)【data类型数据】(只能由一种格式 2000-9-9、2000-09-09) | 根据字符串返回时间类型 |
toString() | 返回string |
2.4.2DateFormat类
主要用来转换时间
时间对象和字符串之间相互转换
// 【1】 string类型转化为date的时间类型
String str="2023-4-15 12:12:12";
//上下格式一样就可以转
DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //【注意new的是实现类】
Date date = format.parse(str);
// 【2】 把时间类型转换为String
DateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //转换的格式预览
format.format(date);
2.4.3 Calendar日历类
主要用来获取时间
// 【1】 获取年月日
Calendar calendar= new GregorianCalendar(); //【注意new的是实现类】
calendar.get(Calendar.YEAR);//获取年
System.out.println(calendar.get(Calendar.MINUTE));
// 【2】 date=>calendar
Date date= new Date()
calendar.setTime(date);
// 【3】 calendar=>date
Date date = calendar.getTime();
2.5枚举
枚举里面创建的是常量,用来对内容限制
比如性别只有男女,可以把男女定义为枚举类型,只能获取枚举类型中的一种
public enum Gender{
男,女
}
public class Student{
private Gender sex;
public Gender getSex(){
return sex;
}
public Gender setSex(Gender sex){
this.sex=sex;
}
}
Student stu=new Student();
stu.setSex(Gender.男);
2.6Arrays类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yvtYeKAg-1681980768120)(…/Asset/image-20230417164918855.png)]
第三章 集合框架
注意!!本阶段有联系题目,在这里
黑马程序员Java零基础视频教程_上部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili
黑马程序员Java零基础视频教程_下部(Java入门,含斯坦福大学练习题+力扣算法题和大厂java面试题)_哔哩哔哩_bilibili
集合特点
- 集合长度可变,【自动扩容】
- 集合不能直接存基本数据类型,需要把他们变为包装类。
体系结构:
- 单列集合(列表)Collection
- List集合:有序(存取顺序)、可重复、有索引
- Set集合:无序、不重复、无索引
- 双列集合(字典)
3.1泛型
可以在编译阶段约束操作的数据类型,并进行检查 【格式<>】
注意:泛型只支持引用数据类型
**注意:**泛型不具备继承性
为什么要引入泛型?:
- 如果没有泛型,认为是object可以添加任何数据,但是获取数据的时候无法直到是什么类型,因此不方便类型转换
**注意:**java的泛型是伪泛型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZoPymdP7-1681980768120)(…/Asset/image-20230416150508927.png)]
3.1.1三种泛型定义
- 泛型类
public class test<E>{ //不确定使用该类的是何种数据类型
}//E可以理解为变量,但不是用来记录数据的,而是纪律数据的类型
public class MyArrayList <E>{
Object[] obj = new Object[10];
int size;
public boolean add(E e){
obj[size]=e;
size++;
return true;
}
public E get(int index){
return (E)obj[index]; //【伪泛型理解类型转换,因此里面实际还是object,只不过添加了个守门员】
}
}
- 方法泛型(只能在本方法中使用)
public <T> void show(T t){
//不确定形参类型
}
public class ListUtil {
//把参数二添加到参数一后面
public static<E> void addAll(ArrayList<E> list,E... e){
for (int i = 0; i < e.length; i++) {
list.add(e[i]);
}
}
}
- 泛型接口
public interface 接口名 <E>{
//实现类给出具体的类型,实现类创建的时候给出具体的类型
}
// 【1】 实现类给出具体的泛型
public class MyImplements implements List<String> {
@Override
public int size() {
return 0;
}
}
// 【2】 创建时给出具体的泛型
public class My<E> implements List<E> {
@Override
public int size() {
return 0;
}
}
3.1.2泛型通配符
==设想一种场景,我们定义一种方法,只希望传递ye fu zi类型的数据,如果用泛型方法,那么非这三种引用类型的数据也可以传递,
解决办法就是泛型通配符,
?
通配符可以进行范围限制
//? extends E:可以传递E和E所有的子类类型
//? super E:可以传递E和E的所有父类类型
public static void method (ArrayList<? extends ye> list){
//该方法可以接受继承了ye或者ye类型的list
}
3.2Collection(–以下章节为单列–)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qRcJOF6i-1681980768121)(…/Asset/image-20230416100330945.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqu2QEfg-1681980768121)(…/Asset/image-20230416100633393.png)]
注意: 上述方法如果泛型是自定义,需要该自定义类中重写equal方法,才能保证contains方法正常使用。
3.2.1遍历方式
- 迭代器遍历
是集合专用的遍历方式
注意点:
- 迭代器遍历完毕,指针不会复位【如需再次遍历,需要创建一个新的迭代器】
- 循环中只能使用一次next方法,【如果需要在一次循环中多次使用next取到的变量需要用中间变量保存一下】
- 迭代器遍历时,不能用集合的方法进行增删改查【如需使用,需要用集合里的方法进行】
- 没有索引过界,只有没找到元素异常
// 【1】迭代器方式 默认指向零索引
Collection<String> coll = new ArrayList<>();
Iterator<String> it =coll.iterator();//获取集合的迭代器
if(it.hasNext()){//遍历集合
String str=it.next();
if(str.equal("bbb")){
it.remove(); //用集合方式删除
}
}
- for增强遍历
注意点:
- 所有的单列集合和数组才能用
- 内部原理是个iterator迭代器
- 修改for中的变量,不会改变集合中的数据
for(String a:list){
sout(s);
}
- lambda表达式遍历
// 【1】匿名内部类实现【底层遍历集合,依次得到每一个元素,把他传给accept方法】
coll.forEach(new Consumer<String>() {
@Override
//s依次表示集合中的每个元素
public void accept(String s) {
System.out.println(s);
}
});
// 【2】 lambda表达式【(方法形参)->{方法体}】【和匿名内部类形式对比记忆】
coll.forEach((String s)->{
System.out.println(s);
});
3.3List集合
Collection的方法List都继承了
有很多索引操作的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ru5oX5vD-1681980768121)(…/Asset/image-20230416104543135.png)]
3.3.1List系列集合中的两个删除方法
List<Integer> list= new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//优先调用实际参数和形参完全一致的方法,
list.remove(1);//可以通过索引删除,也可以通过值删除【删除索引1位置的元素】
Integer i=1;
list.remove(i);//【删除1这个元素】
3.3.2List遍历方式
如果需要删除元素,使用迭代器
如果需要添加元素,使用列表迭代器
如果只需要遍历,增强for或者lambda
如果需要操作索引,用普通for
- 迭代器(collection 父)
- 列表迭代器
// 【1】 是迭代器的子类
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str=it.next();
if("2".equals(str)){
it.add("3"); // 【列表迭代器的添加方法,迭代器没有】
}
}
- 增强for(collection 父)
- lambda表达式(collection 父)
- 普通for
//【就是根据size get方法获取】
for(size){
get(i);
}
3.34ist实现类
两种实现类的区别:
- ArrayList实现类 数组管理 方便查找,增删慢
- LinkedList双向链表管理,查找慢,方便增加
3.4.1ArrayList集合(实现类)
底层:
- 数组结构的,数组默认长度是10【如果利用空参创建的集合,默认长度为0】【添加第一个元素时,才会生成长度为10的数组】
- 当数组添加满了之后,会自动扩容为1.5倍【如果1.5倍放不下,则会以添加后的实际长度为准】
添加元素原码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yk9taeJ7-1681980768121)(…/Asset/image-20230416142216057.png)]

ArrayList<String> list =new ArrayList<>();
list.add("as");
list.add("as");
list.add("as");
list.remove("sda");
list.set(0," ");
list.get(1);
list.size();
for(String i:list)
System.out.println(list.toString());
3.4.2LinkedList集合
底层:
- 双向列表结构,查询 慢,增删块,如果操作的是首位元素,速度很快
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RJYVNwt9-1681980768122)(…/Asset/image-20230416143149491.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U3YerPh7-1681980768122)(…/Asset/image-20230416144845050.png)]
3.4.3迭代器实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I7OffWDd-1681980768122)(…/Asset/image-20230416145607817.png)]
3.5 Set集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LE4Z6qNP-1681980768122)(…/Asset/image-20230416160643669.png)]
3.6Set实现类
Set实现类区别:
- HashSet:存取无序 不重复 无索引
- LinkedHashSet: 【存取有序】 不重复 无索引
- TreeSet:【可排序】 不重复 无索引
集合选择:
- 如果需要去重默认使用HashSet
- 如果要求有序,才使用LinkedHashSet
3.6.1HashSet
底层原理
- 哈希表存储数据
- 哈希表是增删改查性能都较好的结构
哈希表组成:
- jdk8之前 数组+链表
- jdk8开始:数组+链表+红黑树
使用注意点:
- 如果存储的是自定义数据类型,需要重写equals和hashCode方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SufNtJYH-1681980768122)(…/Asset/image-20230416161117421.png)]
**PS:**扩容因子就是扩容时机,以为0.75为例,如果当前元素为16*0.75=12的时候就会扩容(扩容为当前的二倍)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e1us0VnK-1681980768123)(…/Asset/image-20230416161413986.png)]
3.6.2LinkedHashESet
底层原理和父类相同
和父类不同的特点:
- 保证存取的顺序,双链表来保证。
- 添加元素时会链接上个元素,上个元素也会链接下个元素
3.6.3 TreeSet
底层:
- 基于红黑树的数据结构实现的
特定:
- 可以根据元素值大大小排序
- 添加元素的时候默认从小到大的排序
- 自定义类型的排序
// 【1】 默认排序规则 【默认使用这种方式】
public class Student implements Comparable<Student>{
private int age;
private String name;
@Override
public int compareTo(Student o) {
//指定排序规则,例如按照年龄升序【this表示当前,o表示需要比较的】
return this.getAge()-o.getAge();
return this.getName()-o.getName();
}
}
// 【2】 比较器比较,在拆功能键TreeSet对象时候,传递比较器Comparator指定规则【优先使用这种方式】
// o1表示当前元素,o2表示比较的元素
TreeSet<String> treeSet = new TreeSet<>((o1, o2) -> {
int i = o1.length() - o2.length();
i= i==0 ? o1.compareTo(o2):i;//如果长度相同调用默认的排序方法,否则就用新的排序方法
return i; //【如果需要大到小排序,return -i即可】
});
treeSet.add("a");
treeSet.add("asdf");
treeSet.add("sadf");
System.out.println(treeSet);
3.7 Map(–以下章节为双列)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xAVEwnPs-1681980768123)(…/Asset/image-20230417144149291.png)]
3.7.1常见方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9uef8zK2-1681980768123)(…/Asset/image-20230417142428109.png)]
注意:以上方法所有实现类都可以用
// 【1】 put方法的细节
put如果不存在,直接添加键值对,否则会覆盖,并且把覆盖的值返回
3.7.2遍历
- key遍历
// 【1】遍历方法通过key遍历。
Set<string> keys = map.keySet(); //【返回一个set集合】
for (String key : keys){
sout(key);
sout(map.get(key));
}
- key-value对遍历
// 【1】 依次获取每个键值对,通过getkey和getvalue获取每个键和值
Set<Map.Entry<String,String>> entries = map.entrySet(); //java中entry代表键值对
for(Map.Entry<String,String> entry:entries){
sout(entry.getKey());
sout(entry.getValue());
}
- lambda表达式遍历
HashMap<String, String> map = new HashMap<>();
map.put("1","123");
map.put("2","236");
map.put("3","369");
map.forEach((key, value) -> System.out.println(key+"->"+value));
3.8Map实现类
3.8.1 HashMap
方法用map的方法api即可
特点(所有map的特点都和key有关):
- 无序,不重复,无索引
- 底层原理和hashSet的底层一摸一样(只不过需要用到key)
- 如果键存储的时自定义对象,需要重写hashCode和equals方法。值不需要
3.9.2LinkedHashMap
特点:
- 有序,不重复,无索引(实现和LinkedHashSet一样,双向链表来保证)
3.9.3TreeMap
特点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amFF9HIA-1681980768123)(…/Asset/image-20230417150826291.png)]
3.9.4 HashMap和TreeMap的原码(底层原理)
集合进阶-13-HashMap源码超详细解析(一)_哔哩哔哩_bilibili
3.9Collections工具类
基本上都是静态方法
用类名.方法名来做
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RPEIuPtF-1681980768124)(…/Asset/image-20230417152135272.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PiXFKFfi-1681980768124)(…/Asset/image-20230417152210597.png)]
3.10 不可变集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5trMqByH-1681980768124)(…/Asset/image-20230417153016903.png)]
List<String> list = List.of("1234", "1234", "1234", "12343214");
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
- 创建map集合的不可变数组
注意map里面of的参数最多10个,
如果需要传递多个,需要把键值对看作整体,用ofEntries方法
// 【1】 ofEntries方法,传递可变长度键值对
HashMap<String, String> hm = new HashMap<>();
hm.put("a", "1");
Set<Map.Entry<String, String>> entries = hm.entrySet();
//把entries变为数组,指定类型,形参代表具体的类型
//形参这里有个长度,entries集合有个长度,如果entries长度大于数组长度,会重新创建数组,否则不会创建新的数组,直接用
Map.Entry[] arr = entries.toArray(new Map.Entry[0]);
//创建不可变map
Map map = Map.ofEntries(arr);
System.out.println(map);
// 【2】Map.copyOf创建不可变map 【jdk 10之后才支持】
HashMap<String, String> hm = new HashMap<>();
hm.put("a", "1");
Map<String, String> map = Map.copyOf(hm);
第四章 文件和IO流
4.1File
File对象的
- File对象表示一个路径,可以是文件路径 ,也可以是文件夹的路径
- 这个路径可以是存在的也可以是不存在的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEjGQEm1-1681980768124)(…/Asset/image-20230418102036385.png)]
String str="c:\\usersa\\lienware\\Desktop\\a.txtt";
File f1=new File(str);
sout(f1);//
File f1=new File(parent,child);
4.1.1 判断和获取
文件对象.方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYODt9pE-1681980768125)(…/Asset/image-20230418102532352.png)]
4.1.2创建删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1o8zKeJ-1681980768125)(…/Asset/image-20230418102734844.png)]
// 【1】 创建新的空文件
File f1=new File("E:\\a.txt"); //路径存在但是文件不存在【如果不包含后缀,会创建一个无后缀的文件
boolean b=f1.createNewFile(); //【创建不存在的文件】【如果文件存在,则会创建失败】
System.out.println(b);
// 【2】 创建单级文件夹
File f1=new File("E:\\test"); //【只能创建一个文件夹】【路径存在会false】
boolean b=f1.mkdir();
System.out.println(b);
// 【3】 创建多级文件夹
File f1=new File("E:\\test\\test1\\tst3");
boolean b=f1.mkdirs();
System.out.println(b);
// 【删除文件】
File f1=new File("E:\\a.txt");
boolean b=f1.delete(); //【删除不走回收站】【优先删除文件】【如果是文件夹删除空文件夹】
System.out.println(b);
4.1.3 获取和遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HimqsmyQ-1681980768125)(…/Asset/image-20230418103919239.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-piRx9CSW-1681980768125)(…/Asset/image-20230418104114222.png)]
4.2 IO流
读写文件的数据,或者网络中的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNkki7io-1681980768125)(…/Asset/image-20230418145745665.png)]
注意:只有 txt和md文件是纯文本文件
- 体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRWEqi6U-1681980768126)(…/Asset/image-20230418170840712.png)]
4.2.1字符集
详将看 IO流-14-字符集详解(Unicode)_哔哩哔哩_bilibili
4.3字节流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qkqh2XNn-1681980768126)(…/Asset/image-20230418150114855.png)]
- 例子:小文件copy
// 【1】 小文件拷贝
public class CopyText {
public static void main(String[] args) throws IOException {
//创建输入输出流
FileInputStream fis = new FileInputStream("a.txt");
FileOutputStream fos = new FileOutputStream("b.txt");
//先读一个,然后写一个
int b;
while((b=fis.read())!=-1){
fos.write(b);
}
//先开的流后关
fos.close();
fis.close();
}
}
// 【2】 ⭐ 大文件拷贝
4.3.1 FileOutputStream
原理
创建通道[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LhobUFgL-1681980768126)(…/Asset/image-20230418150604497.png)]
传输数据[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qDdO41h7-1681980768126)(…/Asset/image-20230418150614075.png)]
关闭通道[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iE8jZq0d-1681980768127)(…/Asset/image-20230418150625588.png)]
- 创建通道
- 可以是字符串表示的路径,也可以是File对象
- 文件不一定需要纯在(不存在会自动创建),但是父级路径要存在。
- 如果文件已经存在,会清空该文件。
FileOutputStream fs = new FileOutputStream("a.txt");
// 【3】 打破特点3续写开关【第二个参数是续写开关】
FileOutputStream fs = new FileOutputStream("a.txt",true);
- 写入数据
- write参数是整数,但实际上是其对应的字符
//================================write()写入方法=======================================
// 【1】 写入一个数据
fs.write(97);
// 【2】 写入一个数组
public static void main(String[] args) throws IOException {
FileOutputStream fs = new FileOutputStream("a.txt");
byte[] bytes={97,98,99,100,101,123};
fs.write(byte,i,j);//【写入数组下标[i,j]区间的数据】
fs.close();
}
// 【3】 写入一个字符串【用byte数组】 换行写
FileOutputStream fos = new FileOutputStream("a.txt");
//创建一个字符串,然后变为字节数组
String str="sadkfjklsadf";
byte[] bytes = str.getBytes();
String str2="666";
byte[] bytes2=str2.getBytes();
String wrap="\r\n"; //【\r是移动光标到最前面\n是换行】
byte[] bytes3=wrap.getBytes();
fos.write(bytes);
fos.write(bytes3); //【换行操作】
fos.write(bytes2);
- 释放资源
- 如果不释放流,java会一直占用该文件
fs.close();
4.3.2 FileInputStream
- 原理过程和FileOutputStream类似
- 文件不存在会报错
- 创建通道
FileInputStream fis = new FileInputStream("a.txt");
- 读入数据
fis.read();
- 有一个返回值。一个一个度,读到返回读到的值,读不到返回-1,
// 【1】 读取一个数据
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt");
int read = fis.read();
System.out.println((char)read);
fis.close();
}
// 【2】 ⭐循环读取
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("a.txt");
int b;
while( (b=fis.read())!=-1){ //【循环判断这里读了依次,因此读文件的指针会移动】
System.out.print((char)b);
}
fis.close();
}
// 【3】 数组读取 【尽可能读取满整个数组,如果不满,优先覆盖数组前面的数据】
- 关闭通道
fis.close();
4.3.2编码和解码(输入写入中文)
String str="asd上的飞机f";
byte[] bytes = str.getBytes("utf-8");// 用utf-8编码
String str2=new String(bytes,"utf-8");// 用utf-8解码,
4.4字符流
为什么会有乱码:
- 中文没有读取完毕(解决办法就是字符流)
- 编码解码用的不是一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JU7xBMmU-1681980768127)(…/Asset/image-20230418163914851.png)]
4.4.1FileReader
读数据
底层:还是字节流,如果遇到中文,会读取多个字节。
- 创建字符输入流对象
FileReader fr = new FileReader("a.txt");
- 读取数据
read
默认一次读一个字节,遇到中文读取多个字节,然后解码转成10进制(该十进制表示对应字符集上该字符的编码)
// 【1】 空参read()方法读取
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("a.txt");
int read;
while((read=fr.read())!=-1)
System.out.print((char)read);
}
// 【2】 数组读取,【char数组和字节流不同,字节流是bytes数组】
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("a.txt");
char[] chars = new char[2]; //【一次读取两个字符】
int len;
while((len=fr.read(chars))!=-1){
System.out.print(new String(chars,0,len));
}
}
- 释放资源
fr.close();
4.4.2FileWriter
- 创建字符流输出对象
- 参数是字符串或者file对象
- 文件不存会创建,但是父级路径要存在
- 有续写开关
- 写出数据到文件
写出的数据是整数,则最终写出的是该整数在字符集上的结果
// 【1】 写出一个string到文件
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("a.txt",true);//【第二个参数是学些开关】
String str="阿斯顿积分考虑 爱看书的肌肤 发生的纠纷 但是,阿斯顿积分他奥兰多开始发,阿斯利康的飞机 覅放天地方";
fw.write(str,off,len);//从str[0ff]开始写入长度为len
fw.close();
}
- 释放资源
fw.close();
4.4.3底层
- 字符流有缓冲区,字节流没有,每次尽可能装满缓冲区
- 读取一次之后,如果再次new新的流,然后再读,实际上读的是缓冲区的数据。文件中其他数据就不读了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4gJ4xgRu-1681980768127)(…/Asset/image-20230418170222556.png)]
IO流-21-字符输出流的底层原理超详解_哔哩哔哩_bilibili
4.5缓冲流(–以下章节为高级流)
任何有缓冲区的流,再读取大文件的时候,需要刷新缓冲区
4.5.1体系结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zjmF8tg-1681980768127)(…/Asset/image-20230418171020802.png)]
4.5.2字节缓冲流
- 介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-64m3tVrK-1681980768127)(…/Asset/image-20230418171052510.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xA9qt29o-1681980768128)(…/Asset/image-20230418171104531.png)]
- 实例,字节缓冲流copy文件
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt",true));//读入文件
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("my.txt"));
//循环读取,写道目的地
int len;
byte[] bytes=new byte[1024];
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close(); //【会帮我们自动关闭基本流】
bis.close();
}
- 原理
提升效率的原理
- 输入的时候有一个8192的缓冲区,同理输出也有
- 输入输出数据的时候会一次性读入读出 所有缓冲区中的数据
- 我们读取数据的时候实际上是从输入缓冲区和输出缓冲区之间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3qKOcYx-1681980768128)(…/Asset/image-20230419093213823.png)]
4.5.3字符缓冲流
- 介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ODZefjc1-1681980768128)(…/Asset/image-20230419093401619.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NSMNtDbG-1681980768128)(…/Asset/image-20230419093451336.png)]
- 实例
//1创建集合对象 ,以为String为例 JDK7之后
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
String str=it.next();
if("2".equals(str)){
it.add("3"); //迭代器本身的添加方法
}
}
4.6转换流(属于字符流)
InputStreamReader
字符转换输入流(读数据)
OutputStreamWriter
字符转换输出流(写数据)
- 字符流和字节流之间的桥梁[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EyM1Kd6b-1681980768129)(…/Asset/image-20230419094516543.png)]
- 例子
// 【1】 自定字符编码读入写出数据【jdk 10】
FileReader fr = new FileReader("a.txt", Charset.forName("utf-8")); //【第二个参数不能直接写编码,要用Charset.forNmae("")】
FileWriter fw = new FileWriter("asd.txt", Charset.forName("utf-8"));
int ch;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
fw.write(ch);
}
fw.close();
fr.close();
// 【2】 利用字节流读取一行数据
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//创建字节转字符流
BufferedReader br=new BufferedReader(isr);//根据字符流创建字符缓冲流
String str;
while((str=br.readLine())!=null){
System.out.println(str);
}
br.close();
isr.close();
}
4.7序列化/反序列化流(操作对象 属于字节流)
主要用于把对象序列化写入本地/反序列化读取对象
4.7.1序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MtVXO8sn-1681980768129)(…/Asset/image-20230419100859012.png)]
- 序列化例子
Student stu = new Student("asdf", 123);
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("stu.txt"));
os.writeObject(stu); //把对象写入出去
os.close();
public class Student implements Serializable ,Comparable<Student>//【写出的类需要实现Serializable接口,该接口为标记接口无方法】
4.7.2反序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-emGjEGsv-1681980768129)(…/Asset/image-20230419101531609.png)]
- 反序列化例子
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu.txt"));
Student o = (Student) ois.readObject();
System.out.println(o);
4.7.3注意点
- 序列化后,对象如果改变,那么反序列化将会失败
- 序列化的时候把所有的成员变量都序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P9i1HxbL-1681980768130)(…/Asset/image-20230419102634924.png)]
1的解决办法(定义版本号)
// 【1】 第一种办法
public class Student implements Serializable{
private static final long serialVersionUID=1L;//【固定版本号】
private int age;
private String name;
}
// 【2】 第二种办法
//设置idea,然后利用idea自动生成版本号
2的解决办法 如果有不想被序列化的成员变量怎么办
public class Student implements Serializable{
private static final long serialVersionUID=1L;//【固定版本号】
private int age;
private String name;
private transient String address; //【transient修饰的成员变量不会被序列化】
}
// 此时如果反序列化读取到transient修饰的变量时为默认初始值
4.8 打印流(字节流、字符流)
- 打印流只可以把数据写入到文件
- 特有的方法可以实现数据原样写
- 特有方法可以实现自动刷新,自动换行
4.8.1字节打印流
无缓冲区
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IT487jPU-1681980768130)(…/Asset/image-20230419103404550.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJNzN0Pu-1681980768130)(…/Asset/image-20230419103529228.png)]
- 例子
// 【1】
PrintStream ps = new PrintStream(new FileOutputStream("print.txt"), true, Charset.forName("utf-8"));
//写出数据
ps.println("asdfds"+134); //【换行输出】
ps.printf("%s 爱上了 %s","阿珍","阿强"); //【占位符输出】
ps.println();
ps.println(true); //【直接打印true到文件】
ps.close();
4.8.2字符打印流
有缓冲区 ,熟读更快
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HfYqsRXt-1681980768130)(…/Asset/image-20230419104054939.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8b38NOKx-1681980768131)(…/Asset/image-20230419104103677.png)]
- 例子
// 【1】
PrintWriter pw = new PrintWriter(new FileWriter("text1.txt"), true);
pw.println("打开附件赛了可就奥萨蒂撒赖扩大c");
pw.print("你好");
pw.close();
4.9压缩/解压缩流(字节流,输入流,输出流)
传输大文件
4.9.1解压缩流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0pySo6lb-1681980768131)(…/Asset/image-20230419105250434.png)]
// 【1】 解压文件的例子
public class Hello {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File src = new File("E:\\test.zip");
File dest = new File("E:\\");
}
public static void unzip(File src,File dest) throws IOException {
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
//获取到的文件夹或者文件
ZipEntry entry;
while((entry=zip.getNextEntry())!=null){
//如果是文件夹就要在目的地处创建同样的文件夹
if(entry.isDirectory()){
File file = new File(dest,entry.toString());//dest是根目录,第二个参数是需要写的目录
file.mkdirs();//可能是多层级,那么需要mkdirs;
}else{
FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString()));
int b;
while((b=zip.read())!=-1){
fos.write(b);
}
fos.close(); //关闭一个文件
zip.closeEntry(); //表示压缩包里的一个文件已经操作完毕了
}
}
zip.close(); //所有文件和文件夹都处理完毕了
}
}
4.9.2压缩流
IO流-41-压缩流-压缩单个文件_哔哩哔哩_bilibili
4.10常用工具包 Commons-io /hutool
4.10.1 Commons-io
提高io流的效率
如何使用:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4GwrKxs4-1681980768131)(…/Asset/image-20230419145132523.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ML37CZOv-1681980768131)(…/Asset/image-20230419145037898.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FiX06Nik-1681980768131)(…/Asset/image-20230419145140988.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lRpKD5nF-1681980768132)(…/Asset/image-20230419145219658.png)]
4.10.2 Hutool包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GCn1ysas-1681980768132)(…/Asset/image-20230419153745875.png)]
Hutool — 🍬A set of tools that keep Java sweet.
第五章 多线程&JUC
5.1多线程创建基本写法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-34oVqN9c-1681980768132)(…/Asset/image-20230419160609825.png)]
5.1.1继承Thread类的方式 重写run方法
public class myThread extends Thread{
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
System.out.println(getName()+"hello");//获取运行的线程的名字
}
}
}
class test{
public static void main(String[] args) {
myThread t1 = new myThread();
myThread t2 = new myThread();
t1.setName("线程1"); //设置线程名字
t2.setName("线程2");
t1.start();
t2.start();
}
}
5.1.2实现Runable接口,然后实现Run方法
public class myThread implements Runnable {
//Runnable是函数式接口
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
//获取到当前线程对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"hello");//获取运行的线程的名字
}
}
}
class test{
public static void main(String[] args) {
myThread my = new myThread(); //创建对象
Thread thread1 = new Thread(my,"线程名字"); //把实现多线程方法的类放到Thread中
Thread thread2 = new Thread(my); //把实现多线程方法的类放到Thread中
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
5.1.3 第三种多线程,可以获取线程运行的结果
- 实现callable接口
- 重写call有返回值,表示多线程运行结果
- 创建自己的类对象(表示多线程要执行的任务)
- 创建FutureTask的对象
- 创建Thread对象开始线程
public class myThread implements Callable<Integer> {
//接口后面的泛型表示返回的类型
@Override
public Integer call() throws Exception {
int sum=0;
for (int i =0;i<=100;i++){
sum=sum+i;
}
return sum;
}
}
class test{
public static void main(String[] args) throws ExecutionException, InterruptedException {
myThread myThread = new myThread();//创建自己的类
FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(myThread); //多线程管理工具(用于获取执行结果)
Thread thread = new Thread(integerFutureTask); //创建多线程
thread.start();//启动多线程
Integer integer = integerFutureTask.get(); //通过线程管理类获取执行结果
System.out.println(integer);
}
}
5.2Thread常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dYeIiVE9-1681980768132)(…/Asset/image-20230419160751671.png)]
5.2.1设置线程名字
// 【1】 可以用setName(间5.1.1)也可以用构造方法
public class myThread extends Thread{
public myThread() {
super();
}
public myThread(String name) {
super(name);
}
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
System.out.println(getName()+"hello");//获取运行的线程的名字
}
}
}
class test{
public static void main(String[] args) {
myThread t1 = new myThread("飞机:");
myThread t2 = new myThread("坦克:");
t1.setName("线程1"); //设置线程名字
t2.setName("线程2");
t1.start();
t2.start();
}
}
5.2.2 线程优先级
优先级越高,越容易强到cpu
- main优先级最高为1
- 优先级从1-10
public class myThread implements Runnable {
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
//获取到当前线程对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"hello");//获取运行的线程的名字
}
}
}
class test{
public static void main(String[] args) {
myThread my = new myThread(); //创建对象
Thread thread1 = new Thread(my,"飞机"); //把实现多线程方法的类放到Thread中
Thread thread2 = new Thread(my,"af d"); //把实现多线程方法的类放到Thread中
System.out.println(thread1.getPriority());// 获取线程优先级
thread1.start();
thread2.start();
}
}
5.2.3守护线程
其他非守护线程执行完毕之后,守护线程会陆续结束
Thread thread2 = new Thread(my,"af d");
thread2.setDaemon();//其他线程执行完毕后该线程自动停止
5.2.4礼让插入线程(了解)
// 【1】礼让线程
public class myThread implements Runnable {
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
//获取到当前线程对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"hello");//获取运行的线程的名字
Thread.yield();//【出让当前cpu的执行权】【如果不出让当该线程获取到cpu后一直等到别人抢占cpu才会停止】
}
}
}
class test{
public static void main(String[] args) {
myThread my = new myThread(); //创建对象
Thread thread1 = new Thread(my,"飞机"); //把实现多线程方法的类放到Thread中
Thread thread2 = new Thread(my,"af d"); //把实现多线程方法的类放到Thread中
thread1.start();
thread2.start();
}
}
// 【2】 插入线程
public class myThread implements Runnable {
public void run(){
//需要线程执行的代码
for(int i =0;i<100;i++){
//获取到当前线程对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"hello");//获取运行的线程的名字
Thread.yield();//出让当前cpu的执行权
}
}
}
class test{
public static void main(String[] args) throws InterruptedException {
myThread my = new myThread(); //创建对象
Thread thread1 = new Thread(my,"飞机"); //把实现多线程方法的类放到Thread中
Thread thread2 = new Thread(my,"af d"); //把实现多线程方法的类放到Thread中
thread1.start();
thread1.join(); //只有当thread1的线程全部执行完毕,才会接着执行后面的代码;
thread2.start();
}
}
5.3线程
5.3.1线程生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjP4ThqA-1681980768133)(…/Asset/image-20230419162607444.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bTjLIktV-1681980768133)(…/Asset/image-20230420101038243.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ek4SdHTw-1681980768133)(…/Asset/image-20230420101219305.png)]
5.3.2线程安全问题
互斥和同步问题
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-231h4xKk-1681980768133)(…/Asset/image-20230419162949145.png)]
同步代码块不能写道循环外面
// 【1】 同步代码块解决买票安全问题
public class myThread extends Thread {
static int tickets=100;//户次资源
static Object obj = new Object();//锁对象 要是唯一的static修饰
public void run(){
while(true){
synchronized (obj){ //【这里可以用MyThread.class字节码文件对象,是唯一的】
if (tickets>0){
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
tickets--;
System.out.println(getName()+"卖出一张票还剩"+tickets+"张票");
}else{
break;
}
}
}
}
}
class test{
public static void main(String[] args) throws InterruptedException {
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
- 同步方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dkLbRedv-1681980768133)(…/Asset/image-20230419163905894.png)]
// 【2】 同步方法
import javax.swing.plaf.TableHeaderUI;
import java.util.concurrent.Callable;
public class myThread implements Runnable {
int tickets=0;//互斥资源
@Override
public void run(){
while(true){
if(method()) break;
//在同步方法外面睡才能更加清晰的看到多线程效果【千万不要在同步方法里面睡啊,否则还是一个方法执行到底】
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized boolean method() {
if (tickets==100){
return true;
}else{
tickets++;
System.out.println(Thread.currentThread().getName()+"卖出第"+tickets+"张票");
}
return false;
}
}
class test{
public static void main(String[] args) throws InterruptedException {
myThread t = new myThread();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
5.3.3 lock锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWPDS6V8-1681980768134)(…/Asset/image-20230419170039817.png)]
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class myThread extends Thread {
static int tickets=100;//【互斥资源】
static Lock lock= new ReentrantLock(); //【需要保证只有一个对象】
public void run(){
while(true){
lock.lock();//上锁
try {
if (tickets>0){
sleep(100);
tickets--;
System.out.println(getName()+"卖出一张票还剩"+tickets+"张票");
}else{
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
class test{
public static void main(String[] args) throws InterruptedException {
myThread t1 = new myThread();
myThread t2 = new myThread();
myThread t3 = new myThread();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
5.3.4 生产者消费者的实现
- 等待唤醒
//互斥资源
public class Desk {
public static int foodFlag=0;//表示是否有面条
public static int count=10;
public static Object Lock=new Object();
}
// 生产者
public class produce extends Thread{
@Override
public void run() {
while(true){
synchronized (Desk.Lock){
if(Desk.count==0){
break;
}else{
//判断是否有食物,如果有就等待
if(Desk.foodFlag==1){
try {
Desk.Lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
System.out.println("做面条");
Desk.foodFlag=1;
Desk.Lock.notifyAll();
}
}
}
}
}
}
// 消费者
public class consumer extends Thread{
@Override
public void run() {
while(true){
synchronized (Desk.Lock){
if(Desk.count==0){
break;
}else{
//先去判断是否有东西,有就开吃
if(Desk.foodFlag==0){
try {
//没有东西可吃
Desk.Lock.wait();//让当前线程和锁绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
Desk.count--;
System.out.println("吃货正在吃面条,还能再吃"+Desk.count+"!1");
Desk.Lock.notifyAll();
Desk.foodFlag=0;
}
}
}
}
}
}
// 测试
public class Hello {
public static void main(String[] args) {
produce produce = new produce();
consumer consumer = new consumer();
produce.setName("生产则");
consumer.setName("消费者");
produce.start();
consumer.start();
}
}
- 阻塞队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lp1uA3zl-1681980768134)(…/Asset/image-20230420095957422.png)]
public class Hello {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
produce produce = new produce(queue);
consumer consumer = new consumer(queue);
produce.setName("生产则");
consumer.setName("消费者");
produce.start();
consumer.start();
}
}
//生产者
public class produce extends Thread{
ArrayBlockingQueue<String> queue;
public produce(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while(true){
try {
queue.put("面条");// 实际上下面的打印语句在锁的后面,因此结果会出现连续的输出
System.out.println("厨师做了一碗面条");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消费者
public class consumer extends Thread{
ArrayBlockingQueue<String> queue;
public consumer(ArrayBlockingQueue<String> queue) {
this.queue = queue;
}
@Override
public void run() {
while(true){
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5.3.5 线程池
线程池用来管理线程,执行完毕后线程不会消失,基于保存在线程池中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDagjhQF-1681980768134)(…/Asset/image-20230420101512029.png)]
- 代码实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W6xcPUvQ-1681980768134)(…/Asset/image-20230420101551564.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hAsLjAqU-1681980768135)(…/Asset/image-20230420101614450.png)]
// 【1】 工具类创建线程池对象
public class Hello {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
//获取线程池
ExecutorService pool1 = Executors.newFixedThreadPool(3);
//提交任务
pool1.submit(new produce(queue));
pool1.submit(new consumer(queue));
//销毁
pool1.shutdown();
}
}
- 线程池具体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t1KHOCh2-1681980768135)(…/Asset/image-20230420102952964.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2hApAyJA-1681980768135)(…/Asset/image-20230420102906144.png)]
public class Hello {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
//自定义线程池
ThreadPoolExecutor pool1 = new ThreadPoolExecutor(
3,
6,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() //任务拒绝策略是静态内部类,用new 外部类.内部类来创建
);
//提交任务
pool1.submit(new produce(queue));
pool1.submit(new consumer(queue));
//销毁
pool1.shutdown();
}
}
- 线程池参数选择
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hVCQYaRt-1681980768135)(…/Asset/image-20230420103652256.png)]
Java能用多少资源[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dTB8E3Iw-1681980768135)(…/Asset/image-20230420103838449.png)]
5.3.6 多线程额外资料
多线程&JUC-33-多线程的额外扩展内容_哔哩哔哩_bilibili
第六章 注解(Annotation)和反射
6.1注解
- 注解不是程序本身,可以对程序做出解释和注释(comment)没什么区别
- 可以被其他程序读取(编译器)
- @+名字(需要被读取的值),通过反射访问
6.1.1内置注解
- @Override
- @Deprecated (不推荐使用)
- Suppress Warning(“all”) (镇压警告,消除方法/类中的警告)
6.1.2元注解
注解其他注解(在其他注解定义的时候使用)
- @Target 描述注解的适用范围
- @Retention 表示需要什么级别保存该注解信息,用于描述注解的生命周期。(SOURC<CLASS<RUNTIME)
- @Documented 说明该注解被包含在javadoc中
- @Inherited 说明子类可以继承父类中的该注解
// 【1】
@Target(value = ElementType.METHOD)// 只能在方法上
@Retention(value = RetentionPolicy.RUNTIME) //表示我们的注解在运行时还有效
@Documented //我们的注解会生成到javadoc中
@Inherited //子类可以继承父类写的本注解
@interface MyAnnotation{
}
@MyAnnotation //这里会报错,
public class Hello {
@MyAnnotation //自定义的注解写道方法上可以
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
}
}
6.1.3自定义注解
@interface 注解名
@Target(value = ElementType.METHOD)// 只能在方法上
@Retention(value = RetentionPolicy.RUNTIME) //表示我们的注解在运行时还有效
@Documented //我们的注解会生成到javadoc中
@Inherited //子类可以继承父类写的本注解
@interface MyAnnotation{
String name() default "as";//这是注解的参数 删除类型 参数名() 默认值为as
int age() default 0;
int id() default -1;//默认不存在indexof找不到返回-1
String[] Schools();
}
@MyAnnotation(name="asdf",Schools = {"湖南科技大学","南京航空航天大学"},age=18,id = 12341)
public static void main(String[] args){
}
6.2反射
- 把静态变为动态
- 反射机制允许程序在执行期间借助Reflection API【获取任何类】的【任何内部信息】,并直接操作任意对象的内部属性及其方法
- 反射允许对成员变量,成员方法和构造方法的信息进行编程访问(idea的代码补全就是利用反射功能,展示该类的所有方法,包括给出方法的所有形参)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1yZVXUHy-1681980768136)(…/Asset/image-20230420140302125.png)]
- 获取的时候是根据Class对象获取
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qnLe3Oz-1681980768136)(…/Asset/image-20230420134658118.png)]
6.2.1获得Class对象 三种方式
// 【1】 第一种 Class.forName("类全名")【最常用】
public class Hello {
public static void main(String[] args) throws ClassNotFoundException {
Class c1=Class.forName("User"); //【参数是包名+类名的格式】
System.out.println(c1);
}
}
// 【2】 类名.class 【当作参数传递】
public class Hello {
public static void main(String[] args) throws ClassNotFoundException {
Class c1=Student.class;
System.out.println(c1);
}
}
// 【3】 对象名.getclass(); 【已经有了这个类的对象的时候才使用】
public class Hello {
public static void main(String[] args) throws ClassNotFoundException {
Student s= new Student();
Class class=s.getClass();
System.out.println(c1);
}
}
6.2.2利用Class对象获取类的构造方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eXh6rEiS-1681980768136)(…/Asset/image-20230420141007187.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eb1SXgkw-1681980768136)(…/Asset/image-20230420141026649.png)]
// 【1】 获取构造方法
public class Hello {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1=Class.forName("User"); //获取User类的Class对象,一个类被加载后,类的整个结构都会被封装在Class对象中
Constructor[] cons = c1.getConstructors(); //获取public构造方法【不加s获取单个,默认获取】
for (Constructor con:cons){
System.out.println(con);
}
Constructor[] cons2=c1.getDeclaredConstructors(); //获取所有构造方法【不加s获取单个】
Constructor cons3=c1.getDeclaredConstructor(String.class); //获取单个构造方法,其中获取到的是构造方法只有一个String类型形参的那个
for (Constructor con : cons2) {
System.out.println(con);
}
}
}
// 【2】 获取构造方法里面的内容
Class c1=Class.forName("User");
Constructor cons3=c1.getDeclaredConstructor(String.class,int.class);
int modifiers = cons3.getModifiers(); //获取修饰符等级
Parameter[] parameters = cons3.getParameters(); //获取所有参数
cons3.setAccessible(true); //【如果获取到的是私有构造,需要这条语句才可以利用该私有构造创建对象】【暴力反射,暂时忽略权限】
User user = (User) cons3.newInstance("asdf",12); //利用获取到的构造方法创建对象
System.out.println(user);
6.2.3 获取成员变量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P41V7o77-1681980768137)(…/Asset/image-20230420142817089.png)]
// 【1】 获取成员变量
Class c1=Class.forName("User");
Field[] filds=c1.getFields(); //其他方法类似
for (Field fild : filds) {
System.out.println(fild);
}
// 【2】 获取成员变量的内容
public class Hello {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class c1=Class.forName("User");
Field[] filds=c1.getDeclaredFields();
for (Field fild : filds) {
System.out.println("成员变量"+fild);
String name = fild.getName(); //获取该成员变量的名字
System.out.println("成员变量的名字"+name);
Class<?> type = fild.getType(); //获取该成员变量的类型
System.out.println("成员变量的类型"+type);
int modifiers = fild.getModifiers(); //获取该成员变量的修饰等级
System.out.println("成员变量的修饰等级"+modifiers);
User u = new User("name", 11, "男");
fild.setAccessible(true);
Object o = fild.get(u);//获取具体对象的该成员变量的具体值
System.out.println("成员变量的具体值"+o);
// 修改成员变量的具体值
//fild.set(u,"")//u是具体对象 第二个参数是要改为什么
System.out.println("===================================================");
}
}
}
6.2.4获取成员方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jULiCp8s-1681980768137)(…/Asset/image-20230420144014169.png)]
// 【1】写法和6.2.2---6.2.3类似
6.2.5 获取方法的泛型
- 获取方法参数里面的泛型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aGMq4OpX-1681980768137)(…/Asset/image-20230420151924220.png)]
- 获取方法泛型返回值的类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Z98yXYw-1681980768137)(…/Asset/image-20230420152229467.png)]
第七章 JVM
7.1JVM的位置
在操作系统之上,每个操作系统负责实现各自的jvm,这是java可以跨平台的基础。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6fVwpw5k-1681980768137)(…/Asset/image-20230420154428301.png)]
7.2Jvm的体系结构
简单图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gx0Qw1ve-1681980768138)(…/Asset/image-20230413165349580-16816270566443.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ArtGiCAH-1681980768138)(…/Asset/image-20230420154844916.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0V21gb08-1681980768138)(…/Asset/image-20230420154945677.png)]
垃圾只有在堆区和方法区
详细图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xht6CiPA-1681980768138)(…/Asset/image-20230420155153932.png)]
大部分插件都是在执行引擎上下功夫
7.3类加载器
作用:
- 加载Class文件 new Student(); 引用在栈里面,具体在堆里面
Car.class文件加载过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F0omsyPy-1681980768138)(…/Asset/image-20230420155736886.png)]
加载器分类
- 虚拟机自带
- 启动类(跟)加载器 rt.jar 【这里的一般不动】
- 扩展类加载器(app加载器的上层) jre\lib\ext 【
- 应用程序加载器(我们写的类加载器一般都是这个)
层层递进先从应用程序开始找
7.4双亲委派机制
保证安全(步骤):
- 类加载器收到类的加载请求,把请求给到最高层(ROOT)
- 加载器检查是否能够加载当前这个类,能加载就使用,否则就抛出异常,通知子加载器进行加载。
写好的类会层层递进的寻找,如果在上层的加载器中找到了同名类,则会加载该类,并不会加载自己写的类,下图程序会报找不到main函数错误,因为加载的是ROOT下的String类,这是java自带的类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H8zuBBLe-1681980768139)(…/Asset/image-20230420160508729.png)]
注意:更具体的请百度
7.5沙箱安全机制
- java安全模型的核心是java沙河机制,该机制把java代码限定在jvm的特定范围中运行,严格限制代码堆本地系统资源的访问。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d7wQVzYa-1681980768139)(…/Asset/image-20230420161740212.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KuSnNucS-1681980768139)(…/Asset/image-20230420161754864.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QveBM9nk-1681980768139)(…/Asset/image-20230420161802556.png)]
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kW0jmC3L-1681980768140)(…/Asset/image-20230420161855441.png)]
基本组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y9xI7phP-1681980768140)(…/Asset/image-20230420162352820.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oUdjhJhu-1681980768140)(…/Asset/image-20230420162415618.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VDaVgxoO-1681980768140)(…/Asset/image-20230420162708731.png)]
7.6 native本地方法接口
- 带了native修饰的,说明java的作用范围达不到了,会调用底层c语言的功能。
- native方法会进入本地方法栈
⭐补充,调用其他接口
比如调用python可以用socket WebService http等,可以访问其他类编写的程序
比如java调用pyton程序。可以用url的方式调用,在python程序中处理该url请即可
7.7 pc寄存器
每个线程都有一个层序计数器,是线程私有的,就是一个指针,指向方法中的方法字节码,在执行引擎读取下一条指令,是一个非常小的内存空间
7.8 方法区
- 所有线程共享,所有字段和方法字节码,以及一些特殊方法比如构造函数,接口代码也在这里定义,定义的方法的信息都保存在此,是共享区域。
- 这里面还有一个静态方法区,随类一起加载
- 可以理解为把加载的类放到这里
- 还有一个静态常量池,如果类 默认有一些值就加载到这里,实例化该类对象的时候如果没有修改该值,实际上该值是指向方法区中静态常量池的
总的一句话:静态变量,常量,类信息,运行时的常量池,都在该区域,但是实际变量存在堆
7.9 栈/堆
jvm栈溢出
- 递归调用溢出,程序会不让执行
7.10堆内存调优
方法区和堆,大部分都是调堆
三种jvm 我用的是HotSpot虚拟机(window)
新生区/老年去/永久区
GC垃圾回收 常用算法
JMM
名字
System.out.println(“成员变量的名字”+name);
Class<?> type = fild.getType(); //获取该成员变量的类型
System.out.println(“成员变量的类型”+type);
int modifiers = fild.getModifiers(); //获取该成员变量的修饰等级
System.out.println(“成员变量的修饰等级”+modifiers);
User u = new User(“name”, 11, “男”);
fild.setAccessible(true);
Object o = fild.get(u);//获取具体对象的该成员变量的具体值
System.out.println(“成员变量的具体值”+o);
// 修改成员变量的具体值
//fild.set(u,“”)//u是具体对象 第二个参数是要改为什么
System.out.println(“===================================================”);
}
}
}
#### 6.2.4获取成员方法
[外链图片转存中...(img-jULiCp8s-1681980768137)]
```java
// 【1】写法和6.2.2---6.2.3类似
6.2.5 获取方法的泛型
- 获取方法参数里面的泛型
[外链图片转存中…(img-aGMq4OpX-1681980768137)]
- 获取方法泛型返回值的类型
[外链图片转存中…(img-1Z98yXYw-1681980768137)]
第七章 JVM
7.1JVM的位置
在操作系统之上,每个操作系统负责实现各自的jvm,这是java可以跨平台的基础。
[外链图片转存中…(img-6fVwpw5k-1681980768137)]
7.2Jvm的体系结构
简单图
[外链图片转存中…(img-gx0Qw1ve-1681980768138)]
[外链图片转存中…(img-ArtGiCAH-1681980768138)]
[外链图片转存中…(img-0V21gb08-1681980768138)]
垃圾只有在堆区和方法区
详细图
[外链图片转存中…(img-xht6CiPA-1681980768138)]
大部分插件都是在执行引擎上下功夫
7.3类加载器
作用:
- 加载Class文件 new Student(); 引用在栈里面,具体在堆里面
Car.class文件加载过程
[外链图片转存中…(img-F0omsyPy-1681980768138)]
加载器分类
- 虚拟机自带
- 启动类(跟)加载器 rt.jar 【这里的一般不动】
- 扩展类加载器(app加载器的上层) jre\lib\ext 【
- 应用程序加载器(我们写的类加载器一般都是这个)
层层递进先从应用程序开始找
7.4双亲委派机制
保证安全(步骤):
- 类加载器收到类的加载请求,把请求给到最高层(ROOT)
- 加载器检查是否能够加载当前这个类,能加载就使用,否则就抛出异常,通知子加载器进行加载。
写好的类会层层递进的寻找,如果在上层的加载器中找到了同名类,则会加载该类,并不会加载自己写的类,下图程序会报找不到main函数错误,因为加载的是ROOT下的String类,这是java自带的类
[外链图片转存中…(img-H8zuBBLe-1681980768139)]
注意:更具体的请百度
7.5沙箱安全机制
- java安全模型的核心是java沙河机制,该机制把java代码限定在jvm的特定范围中运行,严格限制代码堆本地系统资源的访问。
- [外链图片转存中…(img-d7wQVzYa-1681980768139)]
- [外链图片转存中…(img-KuSnNucS-1681980768139)]
- [外链图片转存中…(img-QveBM9nk-1681980768139)]
- [外链图片转存中…(img-kW0jmC3L-1681980768140)]
基本组件
[外链图片转存中…(img-y9xI7phP-1681980768140)]
[外链图片转存中…(img-oUdjhJhu-1681980768140)]
[外链图片转存中…(img-VDaVgxoO-1681980768140)]
7.6 native本地方法接口
- 带了native修饰的,说明java的作用范围达不到了,会调用底层c语言的功能。
- native方法会进入本地方法栈
⭐补充,调用其他接口
比如调用python可以用socket WebService http等,可以访问其他类编写的程序
比如java调用pyton程序。可以用url的方式调用,在python程序中处理该url请即可
7.7 pc寄存器
每个线程都有一个层序计数器,是线程私有的,就是一个指针,指向方法中的方法字节码,在执行引擎读取下一条指令,是一个非常小的内存空间
7.8 方法区
- 所有线程共享,所有字段和方法字节码,以及一些特殊方法比如构造函数,接口代码也在这里定义,定义的方法的信息都保存在此,是共享区域。
- 这里面还有一个静态方法区,随类一起加载
- 可以理解为把加载的类放到这里
- 还有一个静态常量池,如果类 默认有一些值就加载到这里,实例化该类对象的时候如果没有修改该值,实际上该值是指向方法区中静态常量池的
总的一句话:静态变量,常量,类信息,运行时的常量池,都在该区域,但是实际变量存在堆
7.9 栈/堆
jvm栈溢出
- 递归调用溢出,程序会不让执行
7.10堆内存调优
方法区和堆,大部分都是调堆