Java基本语法

//先赋值在a再加
int b=a++

//先加再赋值
int c=++a

//动态数组 ArrayList
List<Phone> list=new ArrayList<Phone>();





位运算

A = 0011 1100

B = 0000 1101

A&B(同1):0000 1100

A|B(1则1):0011 1101

~B:1111 0010

位计算:

<< *2

// >> /2






Scanner

Scanner scanner = new Scanner(System.in);
//判断是否还有输入
if(scanner.hasNextLine){String s=in.nextLine()}
//next()空格结束
//nextLine()回车结束

//scanner.hasNextInt 判断下次输入是否为整数
//scanner.hasNextFloat 判断下次输入是否为小数






Switch

Switch(data){
case value(value==data):
break;
default:System.out.print("No")
}






While

white(true){
//内容
}






do..while

//先执行后判断
do{
//代码
}while(true)






结束循环

//跳出循环
break;

//结束单轮循环 --跳出外层循环 (外层)outer: continue:outer
continue;






可变参数

test(1,2)

//形参a 要放最后
public void test(int... a){
//a收到为数组
System.out.print(a[0],a[1])
}






数组 – Arrays

int[] a=new int[9];
int[] b={1,2,3,4,5}
String b[]=new String[9];

//数组可封装为参数
public static void printc(int[] arrayc){
System.out.print(arrayc[0])
}

public static int[] reverse(int[] arrays){
int[] c=arrays;
return c
}

//Arrays
//数组赋值--覆盖
Arrays.fill(b,0)//全部替换填充0
Arrays.fill(b,1,3,0)//1~3之间

//输出数组
Arrays.toString(c)

//排序
Arrays.sort(c)

//比较
Arrays.equals



动态数组 – ArrayList

  • 吵创建空参 – 默认长度为0
  • 扩容为当前容量的1.5倍
//自定义类型的数组Objiect,<类型>和前面一样即可省略
ArrayList<object> objects = new ArrayList<>();

//添加
objects.add(Value);

//删除 -- 下标 / Value
objects.remove(1);






多维数组

int a[][]=new int[2][2];
int[][] b={{1,2},{2,3},{3,4},{4,5},{5,6}}
//四行两列
//1 2
//2 3
//3 4
//4 5
//5 6






面向对象

//Student
public class Student {
//静态 -- 表示为一个类
public static void saya(){
System.out.say("1")
}
//非静态 可直接调用静态
public void sayb(){
saya();
System.out.sayc("1")
}
}

//Demo
public class Demo {
public static void main(String[] args) {
//静态直接调用
Student.saya();

//调用非静态需要 实例 化
Student student=new Student();
student.sayb();
}
}






封装

1.私有(private)get/set

Student student=new Stuent();
student.setName("chen");
System.out.print(student.getName())






修饰符

  1. public: 可以被任何类访问。
  2. private: 只能在同一类中访问。
  3. protected: 只能在同一类、同一包中的子类或不同包的子类访问。
  4. 包默认(无修饰符):只能在同一包中访问。






Static

static属于类,非static属于对象

伴随类加载而加载

//静态 – 类名.方法直接调用

  • 静态只能访问静态






代码块

//匿名代码块 --跟随对象产生 -- 使用:可赋初始值
{
System.out.print("匿名代码块")
}

//static -- 跟随类产生 -- 只执行一次
{
System.out.print("静态代码块")
}






String

1.Strin是java定义好的一个类。定义在java.lang包中,所以使用的时候不需要导包。

2.Java程序中的所有字符串文字(例如“abcdefg”),都被实为此类的对象。

3.字符串不可变,它们的值在创建后不能被更改

常用方法

  1. 字符串长度
    • int length(): 返回字符串的长度,即包含的字符数。
  2. 索引访问字符
    • char charAt(int index): 返回指定索引位置的字符。
  3. 截取子字符串
    • String substring(int beginIndex): 从指定索引开始截取子字符串。
    • String substring(int beginIndex, int endIndex): 从指定索引范围内截取子字符串。
  4. 比较字符串
    • boolean equals(Object obj): 比较字符串内容是否相同。
    • boolean equalsIgnoreCase(String str): 忽略大小写比较字符串内容是否相同。
  5. 查找子字符串
    • int indexOf(String str): 查找子字符串的第一次出现位置。
    • int lastIndexOf(String str): 查找子字符串的最后一次出现位置。
  6. 替换字符
    • String replace(char oldChar, char newChar): 替换字符串中的字符。
    • String replace(CharSequence target, CharSequence replacement): 替换子字符串。
  7. 转换大小写
    • String toLowerCase(): 将字符串转换为小写。
    • String toUpperCase(): 将字符串转换为大写。
  8. 去除空白字符
    • String trim(): 去除字符串前后的空白字符。
  9. 分割字符串
    • String[] split(String regex): 根据正则表达式将字符串分割为字符串数组。
  10. 检查前缀和后缀
    • boolean startsWith(String prefix): 检查字符串是否以指定前缀开始。
    • boolean endsWith(String suffix): 检查字符串是否以指定后缀结尾。
  11. 字符序列与字符串互转
    • String valueOf(char[] data): 将字符数组转换为字符串。
    • char[] toCharArray(): 将字符串转换为字符数组。
  12. **intern()**,首先会检查字符串常量池中是否有对应的字符串,如果存在则返回这个字符串的引用;否则会先将字符串添加到字符串常量池中,然后再返回这个字符串的引用


  • 创建

image-20231020004509374

String string1 = new String();

String string2 = new String("chen");

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

byte[] bytes = {91,92,93,94,95};
String string4 = new String(bytes);


StringTable(串池/字符串常量池)

  • 字面量方式为字符串赋值时 – 存在则复用,不存在不参加对象,在串池中创建,这是 Java 中的字符串驻留(String Interning)机制

保存在串池,记录串池里面的地址值

String a = "a";
String b = "b"; //保存串池
String ab = "ab";
String c = a+b; //保存 堆

c == ab //false 堆。串

String s1 = new String("hello");
String s2 = s1.intern(); // 将"hello"添加到字符串池,s2指向池中的字符串,【注意点:如果串池里面存在这个字符,则添加失败】


image-20231020005547966


  • new字符对象

保存在里面,记录的是堆里面的地址值

image-20231020005742246



拼接原理

  • 没有变量 – 不产生多余对象,直接在串池拿取

image-20231020160818231


  • 存在变量

如果有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存

JDK1.8之前 :拼接使用到 StringBuilder

  • 变量 + 字符 = 产生两个对象(StringBuilder+String): 因为s1已经在串池,直接拿出来

  • 例如

        String c1 = "a";
String c2 = "b";
String c3 = "c";
String c4 = c1+c2+c3;

//产生四个对象,两个StringBuilder 和 两个 String (toString底层就是 new String)
//1.c1+c2,创建 StringBuilder 从 串池拿取数据进行拼接 tostring 生成 c1+c2 String对象
// (c1+c2)+c3,创建 StringBuilder 从 串池拿取(c1+c2)数据进行拼接 tostring 生成 (c1+c2)+c3 String对象

image-20231020161003288


JKD1.8 ==> 系统会预估要字符串拼接之后的总大小,把要拼接的内容都放在数组中,此时也是产生一个新的字符串

image-20231020162258053

image-20231020162804899



比较

  • == 比较的是地址

基本类型:比较的是具体的数据值

引用数据类型:比较的是地址值

image-20231020010009754



  • 字符串比较值

image-20231020010258913


  • 字符串遍历
String str = "辰呀CP";
for (int i = 0; i < str.length(); i++) {
System.out.println(str.charAt(i));
}

拼接:会产生较多的字符串对象【+ 拼接一次产生一个对象】,影响内存



StringBuilder

  • 可以看成一个容器,创建之后里面的内容是可变的,不会使用串池
  • 可使用链式编程
  • 默认容量 16,扩容 2倍+2,如果还不满足,则为实际的数据的长度

使用场景:拼接、反转


  • 构造方法:

image-20231020141514318


  • 成员方法

image-20231020141621635



StringJoiner

  • 可以定义间隔符、前缀和后缀,方便拼接
  • 构造方法:

image-20231020143710465


  • 成员方法

image-20231020143747205

StringJoiner stringJoiner = new StringJoiner("-","[","]");
stringJoiner.add("1");
String string = new StringJoiner(",").add("cc").toString();


StringBuffer

  • 扩容
    扩容的逻辑就是创建一个新的 char 数组,将现有容量扩大一倍再加上2,如果还是不够大则直接等于需要的容量大小。扩容完成之后,将原数组的内容复制到新数组,最后将指针指向新的 char 数组。

  • 常用方法

StringBuffer append(xxx):拼接字符串
StringBuffer delete(int start,int end):删除指定范围的内容,左开右闭
StringBuffer replace(int start, int end, String str):替换指定范围的内容
StringBuffer insert(int offset, xxx):在指定位置插入指定的内容
StringBuffer reverse() :把当前字符序列逆转




比较与总结

image-20231020143941735

  • String
  1. String是被final修饰的类,不能被继承;
  2. String实现了Serializable和Comparable接口,表示String支持序列化和可以比较大小;
  3. String底层是通过char类型的数据实现的,并且被final修饰,所以字符串的值创建之后就不可以被修改,具有不可变性。
  • 面试题

面试题:String、StringBuffer和StringBuilder的异同?

相同点:底层都是通过char数组实现的

不同点:

  • String对象一旦创建,其值是不能修改的,如果要修改,会重新开辟内存空间来存储修改之后的对象;而StringBuffer和StringBuilder对象的值是可以被修改的;
  • StringBuffer几乎所有的方法都使用synchronized实现了同步,线程比较安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder 没有实现同步,线程不安全,在多线程系统中不能使用 StringBuilder,但是效率比较高。
  • 如果我们在实际开发过程中需要对字符串进行频繁的修改,不要使用String,否则会造成内存空间的浪费;当需要考虑线程安全的场景下使用 StringBuffer,如果不需要考虑线程安全,追求效率的场景下可以使用 StringBuilder。





面向对象

Person person = new Person(); // 创建一个Person类的对象



抽象类 - abstract

  • 1.不能new 这个抽象类,只能靠子类去实现,约束!

  • 2.抽象类中可以写普通方法

  • 3.抽象方法必须在抽象类中

public abstract class chouxianglei{
//抽象方法,只有方法名字,没有方法的实现
public abstract void print();
void print();
}





接口 – interface

可以多继承多接口

作用:
1,约束
2,定义一些方法,让不同的人实现10–>1
3.public abstract //void save();
4.public static final //int age=18;
5,接口不能被实例化
,接口中没有构造方法~
6.implements可以实现多个接口
7,必须要重写接口中的方法~

//接口中的所有的定义都是抽象的  public abstract
public abstract void save();
void save();
int age=18;


//实现类 Impl -- implement -- 要求重写接口中的方法





继承 – extends

​ //单继承

​ //子类继承了父类,就会拥有父类的全部方法(共有),不需要定义,直接实例化子列进行方法的调用


1.Object类

​ //所有类默认继承Object类


2.Super

super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个
2.super必须只能出现在子类的方法或者构造方法中!
3.super和this不能同时调用构造方法!
Vs this
代表的对象不同:
this:本身调用者这个对象
super:代表父类对象的应用
前提
this:没哟继承也可以使用
super:只能在继承条件才可以使用
构造方法
this();本类的构造
super():父类的构造!

image-20230920163549970

//this 指向当前类
//super 指向父类

this.name; this.printc();
super.name; super.printc();


//super(); 和无参构造方法一样被隐藏
super();//调用父类无参构造方法


3.重写

重写:需要有继承关系,子类重写父类的方法!
1.方法名必须相同
2.参数列表列表必须相同
3.修饰符:范围可以扩大但不能缩小:public>Protected>Default>private
4.抛出的异常:范围,可以被缩小,但不能扩大;ClassNotFoundException–>Exception(大)
重写,子类的方法和父类必要一致;方法体不同!


①当父类和子类方法都是静态方法时,方法的调用只和左边定义的数据类型有关

//父类
public class B{
private static void test(){
System.out.print("B")
}
}

//子类
public class A extend B{
private static void test(){
System.out.print("A")
}
}


//test
A a=new A();
a.test(); //-->输出 A;

//父类的引用指向子类
B b =new A();
b.test(); // --> 输出B;

//b是a new出来的对象,因此调用a的方法
//静态方法是类的方法,非静态方法是对象的方法
//静态时,调用b类的方法,b是用b类定义的
//非静态时,b调用对象的方法,b是a类new出来的对象

①当父类和子类方法都是非静态 –>重写 父类的方法

//父类
public class B{
private void test(){
System.out.print("B")
}
}

//子类
public class A extend B{
@Override //重写
private void test(){
System.out.print("A")
}
}


//test
A a=new A();
a.test(); //-->输出 A;

B b =new A();
b.test(); // --> 输出A;






多态

多态注意事顶:
1.多态是方法的多态,属性没有多态
2.父类和子类,有联系类型转换异常!CLassCastException/
3。存在条件:继承关系,方法需要重写,父类引用指向子类对象!(Father f1 new Son();)

  1. 成员变量: 使用的是父类的
  2. 成员方法: 由于存在重写现象,所以使用的是子类的
  3. 静态成员: 随着类的加载而加载,谁调用就返回谁的



类型转换

  • 判断 a instanceof A

  • JDK14:判断是否为A类型,是则转 : a instanceof A aa






内部类

  • 内部类是定义在另一个类OuterClass内部的类。内部类可以访问其外部类的成员变量和方法
public class OuterClass {

private int outerData = 10;

// 内部类
public class InnerClass {
//成员方法
public void display() {
System.out.println("Data from OuterClass: " + outerData);
}
}

public void outerMethod() {
System.out.println("Outer method is called.");

// 在外部类的方法中实例化内部类
InnerClass inner = new InnerClass();
inner.display();
}

public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.outerMethod();

// 在外部类外部实例化内部类
OuterClass.InnerClass inner = outer.new InnerClass();
inner.display();
}
}



静态内部类

静态内部类是定义在另一个类内部,并使用关键字 static 修饰的内部类。它不依赖于外部类的实例,可以像独立的类一样使用。

public class OuterClass {

// 外部类的成员变量
private int outerData = 10;

// 静态内部类
public static class StaticInnerClass {

// 静态内部类的成员变量
private int innerData = 5;

public void displayOuterData(OuterClass outer) {
System.out.println("Outer Data: " + outer.outerData);
}
}

public static void main(String[] args) {
// 创建静态内部类的实例
StaticInnerClass inner = new StaticInnerClass();

// 调用静态内部类的方法
inner.displayOuterData(new OuterClass());
}
}



局部内部类

局部内部类是定义在方法内部的类,它只在该方法内部可见

public class OuterClass {

// 外部类的成员方法
public void outerMethod() {
final int outerData = 10; // 局部变量,必须是 final 或等效 final 的

// 局部内部类
class LocalInnerClass {
public void displayOuterData() {
System.out.println("Outer Data: " + outerData);
}
}

// 创建局部内部类的实例
LocalInnerClass inner = new LocalInnerClass();

// 调用局部内部类的方法
inner.displayOuterData();
}

public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.outerMethod();
}
}



匿名内部类

  • 匿名内部类是一种在声明和创建的同时定义类的方式,通常用于创建临时的、一次性的类实例
public class AnonymousInnerClassExample {

interface Greeting {
void greet();
}

public static void main(String[] args) {
// 使用匿名内部类创建一个实现Greeting接口的对象
Greeting greeting = new Greeting() {
@Override
public void greet() {
System.out.println("Hello, world!");
}
};



// 调用匿名内部类对象的方法
greeting.greet();
}
}

定义了一个接口 Greeting,它有一个 greet 方法。然后,在 main 方法中,我们使用匿名内部类创建了一个实现 Greeting 接口的对象,并在匿名内部类中实现了 greet 方法。我们没有显式地声明一个具体的类,而是直接在创建对象的同时定义了它。


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

//5.匿名内部类,没有类的名称,必须借助接口或者父类,没有名字初始化类 --不用将实例保存到变量中
ILike iLike=new ILike() {
@Override
public void lambda() {
System.out.println("I Like Lambda4");
}
};
iLike.lambda()

new 类名或接口名(){
//重写方法(实现接口的方法)

//此{里面的即是匿名内部类,如果new 的是类,则是继承关系,如果是接口,则是实现关系}
}

method(new Swim){
@Override
public void swimming(){

}
}


}

public static void method(Swim s){
s.swimming();
}
}

interface Swim{
public abstract void swimming();
}







遍历

迭代器遍历

迭代器在java中的类是Iterator,迭代器是集合专用的遍历方式。

  • 一次性,想要一次遍历后复位,需要重新创建迭代器

image-20230921113803272

Collection<String> collection = new ArrayList<>();

collection.add("a");
collection.add("b");

//迭代器对象 -- 好像箭头指向0位置
Iterator<String> iterator = collection.iterator();

while (iterator.hasNext()){ //判断下一位有没有元素
//next 获取元素 和 移动指针
String next = iterator.next();
}

为什么使用

  1. 遍历统一性:迭代器提供了一种统一的遍历方式,无论是遍历集合、列表、集还是映射,都可以使用相似的迭代器接口。这使得代码更具通用性和可维护性。
  2. 避免并发问题:在多线程环境下,使用迭代器可以帮助防止并发问题。迭代器通常在创建时绑定到特定集合,因此多个线程可以同时使用不同的迭代器遍历集合,而不会出现冲突。
  3. 支持删除操作:迭代器通常提供了安全的删除元素的方法,允许在遍历过程中从集合中删除元素而不会破坏遍历的状态。这是使用基本 for 循环时不容易实现的。
  4. 性能优势:对于某些集合实现,迭代器可能会更高效,特别是在遍历大型集合时。迭代器的内部实现可以针对特定集合类型进行优化。
  5. 保护封装性:使用迭代器可以隐藏集合的内部实现细节,从而提供更好的封装性。这使得集合类可以更灵活地修改其内部实现而不影响外部代码。
  6. 不需要索引:迭代器允许您遍历集合而无需关心索引或位置。这对于不支持随机访问的集合(如链表)非常有用。


增强循环

//for-Each 无下标
int[] c= {1,2,3,4,5};
for (array:c){
System.out.println(array);
}

//
Set<String> set = new HashSet<>();
set.add("chen");
for (String s : set) {
System.out.println(s);
}





Lambda表达式

image-20230628214936098

  • 函数式接口
    • 只有一个抽象方法

  • 原写法
public class TestLambdal {
public static void main(String[] args) {
ILike iLike = new Like();
iLike.lambda();
}
}

//1.定义一个函数式接口
interface ILike{
void lambda();
}

//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I Like Lambda");
}
}



  • 推导lambda写法
public class TestLambdal {
public static void main(String[] args) {
//用Lambda简化
ILike iLike = () ->{
System.out.println("I Like Lambda5");
};
iLike.lambda();
}
}

定义一个函数式接口
interface ILike{
void lambda();
}


  • 简化写法
public class TestLambda2 {

public static void main(String[] args) {
//lambda
CP cpe=null;

//原型
cpe=(int a)->{
System.out.println("lambda : cpe-->" + a);
};

//简化1.去除参数类型 ------------ 推荐 -----------------------------
cpe = (a)->{
System.out.println("lambda : cpe-->" + a);
};

//简化2.简化括号
cpe = a -> {
System.out.println("lambda : cpe-->" + a);
};

//简化3.去掉花括号
cpe = a-> System.out.println("lambda : cpe-->" + a);

}
}

interface CP{
void cpe(int a);
}

总结:

  • lambada表达式只能有一行代码的情况下才能简化成一行,如果有多行,那么就用代码块包裹

  • 前提是接口为函数式接口

  • 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号(a,b),






System

  • 获取当前时间结点:currentTimeMillis
long start = System.currentTimeMillis()

  • 拷贝数组
System.arraycopy(数据源组,起始索引,目的地数组,起始索引,拷贝个数)





正则表达式

.matches(“”)

image-20230920210444152

{number}:出现次数

?:可有可无

[区间]

//获取正则表达式的对象
Pattern p = Pattern.compile(正则表达式)

//获取文本匹配器的对象
Matcher m = p.matcher(s)l





异常

使用throws抛出异常,则上一层方法要处理该异常,否则继续往上抛

例如:在实现类中不处理异常,选择往上抛,则接口要处理异常,如果不处理则要继续往上抛,直接抛到有人处理或者到顶层方法(main)处理,main不可继续往上抛

  • 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理

  • 在多重catch:块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常

  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常

  • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出

  • 具体如何处理异常,要根据不同的业务需求和l异常类型去决定

  • 尽量添加finally语句块去释放占用的资源

  • try - catch - finally – 捕捉并处理

    (最高异常类型 Throwable 异常都可捕捉到)

    image-20230412120716788

    //捕捉多个异常,异常类型从小到大
    try{//监控区
    //代码块
    }catch(异常类型 e){
    //异常处理代码模块--关闭/回滚

    }catch(ArithmeticException e){//可捕捉多个异常
    e.printStackTrace();

    }finally{//处理后续工作(可无)
    //释放资源

    }


    最全最详细的Java异常处理 CSDN博客

  • throw – throws 抛出异常不处理

    //假设在这个方法中,处理不了这个异常,方法上抛出异常throws --在catch进行捕捉
    public void test(int a,int b) throws ArithmeticException{
    if(b==0){
    throw new ArithmeticException();//主动抛出异常,一般在方法中使用--throw
    }
    }

    自定义异常

    //定义一个类继承Exception
    public class CException extends Exception{
    private int a;
    public MyException(int a){
    this.a=a;
    }
    //toString:异常的打印信息
    @Override
    public String toString(){

    }
    }



    //使用
    //方法中
    throw new CException(a)

    try{

    }catch(CException e){

    }





常用API

Date

//对象
Date time = new Date()
Date time = new Date(0L)//原始时间过0秒

//修改时间
time.setTime(过久L)

//获取时间的毫秒值
time.getTime()

//获取当前时间
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.format(new java.util.Date());


Arrays

//将数组转为字符串
Arrays.toString(数组)

//二分查找 -- 返回索引
Arrays.binarySearch(数组,值)


//拷贝数组
int[] a = Arrays.copyOf(数组,新数组的长度) //长度小则部分拷贝,长则写入默认值0
int[] a = Arrays.copyOf(数组,start,end) //指定范围


//排序
//第二个参数是一个接口,所以我们在调用方法的时候,需要传递这个接口的实现类对象,作为排序的规则。只使用一次--》匿名内部类
Arrays.sort(arr,规则)
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});





Collection

image-20230921110110464

image-20231002165011606

//Collection是一个接口,我们不能直接创建他的对象。且Collection是 list 和 set 的共性接口
//使用方法只能创建他实现类的对象。
//实现类:ArrayList
Collection<String> collection = new ArrayList<>();

//添加
//①:List返回true,可重复
//②:Set不可重复
collection.add("abc");

//清空
collection.clear();

//删除 -- 共性接口 只能删除对象不能根据索引删除
collection.remove("abc");

//判断是否存在 -- 底层是euqals方法(地址值判断) -- 如果是自定义对象,在javabean中要重写equals方法(生成)
collection.contains("abc");

//判断是否为空
collection.isEmpty();

//获取长度
collection.size();





List集合体系

  1. ArrayList:基于动态数组实现的List集合。
  2. LinkedList:基于双向链表实现的List集合。
  3. Vector:一个古老的同步(线程安全)的List实现,不太推荐使用。
  4. CopyOnWriteArrayList:一个线程安全的List,适合读多写少的情况。


ArrayList

  • ArrayList 底层是基于数组实现的,ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
List<String> list = new ArrayList<>();
// 添加元素
list.add("Apple");
Collections.addAll(list,"a","b","c","d","e");
// 获取元素
String firstElement = list.get(0);
// 修改元素
list.set(1, "Orange");
// 删除元素
list.remove(1); // 删除索引为1的元素 "Banana"

  1. 添加元素
    • boolean add(E e):将元素添加到ArrayList的末尾。
    • void add(int index, E element):在指定的位置插入元素。
  2. 获取元素
    • E get(int index):获取指定位置的元素。
  3. 修改元素
    • E set(int index, E element):将指定位置的元素替换为新元素。
  4. 删除元素
    • boolean remove(Object o):删除第一个匹配指定元素的项。
    • E remove(int index):删除指定位置的元素。
  5. 检查元素是否存在
    • boolean contains(Object o):检查列表是否包含指定元素。
    • boolean isEmpty():检查列表是否为空。
  6. 获取列表大小和容量
    • int size():返回列表中的元素数量。
    • int capacity():返回ArrayList的当前容量。
    • void ensureCapacity(int minCapacity):确保ArrayList的容量至少为minCapacity。
    • void trimToSize():将ArrayList的容量调整为其当前大小。
  7. 清空列表
    • void clear():删除ArrayList中的所有元素。
  8. 获取子列表
    • List<E> subList(int fromIndex, int toIndex):获取指定范围的子列表。
  9. 查找元素索引
    • int indexOf(Object o):返回指定元素的第一个匹配项的索引。
    • int lastIndexOf(Object o):返回指定元素的最后一个匹配项的索引。
  10. 将ArrayList转换为数组
    • Object[] toArray():将ArrayList转换为Object类型的数组。
    • T[] toArray(T[] a):将ArrayList转换为指定类型的数组。


LinkedList

基于双向链表数据结构的列表

实现了List接口,LinkedList还额外实现了Deque接口,正因为 LinkedList 实现了 Deque 接口,所以LinkedList 还可以当作队列来使用。

LinkedList 支持列表操作,队列操作以及双端队列操作,因此可以用于多种不同的情况。


LinkedList<String> linkedList = new LinkedList<>();

常见操作:

  1. 添加元素:
    • add(element): 在链表末尾添加元素。
    • addFirst(element): 在链表开头添加元素。
    • addLast(element): 在链表末尾添加元素。
  2. 获取元素:
    • getFirst(): 获取链表的第一个元素。
    • getLast(): 获取链表的最后一个元素。
    • get(index): 根据索引获取元素。
  3. 遍历链表:
    • 使用增强型 for 循环或迭代器遍历。
  4. 删除元素:
    • removeFirst(): 删除第一个元素。
    • removeLast(): 删除最后一个元素。
    • remove(index): 删除指定索引位置的元素。
  5. 判断是否包含某个元素:
    • contains(element): 判断链表是否包含指定元素。
  6. 获取链表的大小:
    • size(): 获取链表的大小。
  7. 清空链表:
    • clear(): 清空链表中的所有元素。


Vector

  • 支持线程安全的操作
Vector<String> vector = new Vector<>();
// 添加元素
vector.add("Apple")
// 获取元素
String firstElement = vector.get(0);
// 删除元素
vector.remove("Banana");
  1. 添加元素
    • addElement(Object obj):将指定的元素添加到Vector的末尾。
    • add(int index, Object element):在指定的位置插入元素。
  2. 获取元素
    • elementAt(int index):获取指定位置的元素。
    • firstElement():获取第一个元素。
    • lastElement():获取最后一个元素。
  3. 修改元素
    • set(int index, Object element):将指定位置的元素替换为新元素。
  4. 删除元素
    • removeElement(Object obj):从Vector中删除指定元素的第一个匹配项。
    • removeElementAt(int index):删除指定位置的元素。
    • clear():删除Vector中的所有元素。
  5. 容量控制
    • capacity():返回Vector的当前容量。
    • ensureCapacity(int minCapacity):确保Vector的容量至少为minCapacity。
    • trimToSize():将Vector的容量调整为其当前大小


CopyOnWriteArrayList

//1.  
List<String> list = new Vector<>();

//2.
List<String> list = Collections.synchronizedList(new ArrayList<>());

//3. CopyOnWrite:写入时复制。写入的时候复制一份,写入完再插入,避免覆盖
List<String> list = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList 与 Vector

  • Vector的存在add方法是synchronized的,效率比较低
  • CopyOnWriteArrayList 的add方法不是同步方法,使用的是lock锁





Set集合体系

  1. HashSetjava.util.HashSet是Set接口的一个常见实现类,它使用哈希表来存储元素,不保证元素的顺序,而且不允许重复元素。由于哈希表的性质,HashSet的查找、插入和删除操作通常都具有很高的性能。
  2. LinkedHashSetjava.util.LinkedHashSet是HashSet的子类,它保持了元素插入的顺序,可以用来实现有序的集合。LinkedHashSet在迭代时按插入顺序访问元素。
  3. TreeSetjava.util.TreeSet是Set接口的另一个实现类,它基于红黑树(Red-Black Tree)实现,能够保持元素的自然顺序或根据指定的排序规则对元素进行排序。TreeSet是有序的,但要求元素必须是可比较的(实现了Comparable接口或使用Comparator进行比较)。

image-20231002164859765



使用场景

image-20231006203545917



HashSet

哈希值

对象的整数表现形式

1.如果没有重写hashCode方法,不同对象计算出的哈希值是不同

2.如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样

3.但是在小部分情况下,不同硒属性值或者不同的地址值计算出来的哈希值也有可能一样。(哈希碰撞)

image-20231002205437086



image-20231002205552191


  • 表示不包含重复元素的集合
// 创建一个HashSet
Set<String> set = new HashSet<>();

// 添加元素
set.add("Apple");

// 删除元素
set.remove("Apple");

// 检查元素是否存在
boolean contains = set.contains("Banana");

// 检查Set是否为空
boolean isEmpty = set.isEmpty();

// 获取Set的大小
int size = set.size();

// 遍历Set(使用迭代器)
for (String element : set) {
System.out.println(element);
}

// 清空Set
set.clear();
  1. 添加元素
    • boolean add(E e):向Set中添加元素,如果元素已存在,则不会重复添加。
  2. 删除元素
    • boolean remove(Object o):从Set中删除指定元素,如果元素存在且删除成功,则返回true。
    • void clear():删除Set中的所有元素。
  3. 检查元素是否存在
    • boolean contains(Object o):检查Set中是否包含指定元素。
    • boolean isEmpty():检查Set是否为空。
    • int size():返回Set中的元素数量。
  4. 遍历集合
    • Set接口不提供直接索引访问元素的方法,通常需要使用迭代器或转换为List等方式进行遍历。
  5. 集合操作
    • boolean addAll(Collection<? extends E> c):将另一个集合中的所有元素添加到当前Set中。
    • boolean removeAll(Collection<?> c):从当前Set中移除与另一个集合中相同的所有元素。
    • boolean retainAll(Collection<?> c):保留当前Set中与另一个集合中相同的元素,删除其他元素。
    • boolean containsAll(Collection<?> c):检查当前Set是否包含另一个集合中的所有元素。
  6. 转换为数组
    • Object[] toArray():将Set转换为Object类型的数组。
    • <T> T[] toArray(T[] a):将Set转换为指定类型的数组。
  7. 迭代器
    • Iterator<E> iterator():返回用于迭代Set的迭代器。
  8. 比较集合
    • boolean equals(Object o):比较Set与另一个对象是否相等。
    • int hashCode():返回Set的哈希码值。


LinkedHashSet

image-20231004135539467

Set<Student> set = new LinkedHashSet<>();

image-20231004140345856



TreeSet

  • 和Collection的API类似

image-20231006194211014

image-20231006194238362


比较规则

  • 第一种

image-20231006200156306

public class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
//自定义排序规则
/**
* this:表示当前要添加的元素
* o:表示已经在红黑树存在的元素
* 返回值:
* 负数:认为要添加的元素是小的,存左边
* 正数:认为要添加的元素是大的,存右边
* 0:认为要添加的元素已经存在,舍弃
*/
return this.getAge() - o.getAge();
}


  • 第二种

image-20231006200218658

TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {

//按长度进行排序
int i = o1.length() - o2.length();
//如果长度一样,按首字母
i = i==0 ? o1.compareTo(o2) : i;
return i;
}
});



总结

image-20231006203450417






Java Doc文档

/**
* @author 作者
* @version 版本号
* @since 指明需要最早使用的jdk版本
* @param 参数名
* @version 返回值情况
* @throws 异常抛出情况
*/

zh_CN
-encoding utf-8 -charset utf-8





泛型

长图_2023-10-01



  • 约束:声明当前的指定类型,进行统一数据类型,方便代码编辑的时候进行代码检查,获取数据不需要强转了

    1、泛型中不能写基本数据类型 – 使用包装类

    2、指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型

    3、如果不写泛型,类型默认是Object

image-20230928160757710

image-20230928162353955






泛型类

image-20230928163146671

image-20230928163353121

public class MyArrayList<E> {
Object[] objects = new Object[10];
int size;
public void add(E e){
objects[size] = e;
size++;
}
public E get(int s){
return (E) objects[s];
}
public String toString(){
return Arrays.toString(objects);
}
}





泛型方法

image-20230930163729547

public class ListUtil{
private ListUtil() {}

//泛型方法 泛型写在返回值的前面
public static<E> void addAll(ArrayList<E> list,E...e){
for (E element : e) {
list.add(element);
}
}
}





泛型接口

image-20231001133433025

//1.实现类给出具体的类型
public class MyArrayList2 implements List<String>{
}


//实现类延续泛型,创建实现类对象时再确定类型
public class MyArrayList2<E> implements List<String>{
}





通配符

泛型不具备继承性,但是数据具备继承性

  • 泛型里面写的是什么类型,那么只能传递什么类型的数据。
  • 要想实现传递 子父类型使用通配符
public class GenericDemo {
public static void main(String[] args) {
ArrayList<YE> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
ArrayList<Student> list4 = new ArrayList<>();
method(list1); method(list2);method(list3);method(list4);
}

/**
* ? 也表示不确定的类型
* 他可以进行类型的限定
* ? extends E:表示可以传递E或者E所有的子类类型
* ? super E:表示可以传递E或者E所有的父类类型
*/
public static void method(ArrayList<? extends YE> e){}
}

class YE{ }
class Fu extends YE{}
class Zi extends Fu{}
class Student{}





枚举

  • 枚举:用于表示一组有限的常量值
  • 常量可以设置属性定义方法
  • 常量的属性实际就是 方法的属性,需要定义和构造、set和get
  • 枚举类中,每个常量可以有自己的属性,如果不设置属性,外部获取的当前常量,则返回的值是当前常量的值,且不需要设置构造函数和get和set

枚举常量的构造函数不能使用privatepublicprotecteddefault等访问修饰符,也不能显式声明为privatepublic方法。枚举常量的构造函数默认为private,这意味着它们只能在枚举内部使用。这是因为枚举常量在枚举类之外不应该被直接创建或访问,而是由编译器隐式管理。

public enum RCode {

UNKOWN(0,"请稍后重试"),
SUCC(1000,"请求成功"),

private int code;
private String text;

RCode(int code,String text){
this.code=code;
this.text=text;
}

public int getCode() {
return code;
}

public void setCode(int code) {
this.code = code;
}

public String getText() {
return text;
}

public void setText(String text) {
this.text = text;
}
}
RCode smsCodeGivecode = RCode.SUCC;
System.out.println(smsCodeGivecode.getCode()); //1000
System.out.println(smsCodeGivecode.getText()); //请求成功





数据结构



二叉树

image-20231002133858679


遍历

①前序遍历:根 左 右

②中序遍历:左 根 右

③后序遍历:左 右 根

④层序遍历:一层一层的去遍历



平衡二叉树

规则:任意节点左右子树高度差不超过1

image-20231002152120354



左旋

  • 左旋:让出的给节点

image-20231002152405670

image-20231002152728278



右旋

  • 右旋:让出的给节点

image-20231002153100502



image-20231002154036774



红黑树

image-20231002154657909



规则

image-20231002155841253



添加节点

image-20231002163749711






Map

image-20231020164809771


HashMap

  • 存取位置不一致

  • 默认创建长度为 16 加载因子为 0.75 的数组

  • Entry对象记录键值对

  • 利用键计算哈希值决定存储索引

  • null则添加,key一样则覆盖,key不一样,出现哈希碰撞的话,

JDK8 之前:

  • 新数据添加到数组中,而旧数据挂载在新数据下面,形成链表
  • 在JDK 1.7中,HashMap的扩容机制是在扩容时将桶的数量翻倍

JDK8

  • 新数据挂载在旧数据下面,形成链表,当链表长度超过 8 && 数组长度>64的时候,链表自动转为红黑树


  • 遍历

1、获取全部键,再通过键获取值

HashMap<String, String> map = new HashMap<>();
//获取全部键
Set<String> set = map.keySet();
for (String s : set){} //增强for

Iterator<String> iterator = set.iterator(); //迭代器
while (iterator.hasNext()){
System.out.println(iterator.next());
}

map.forEach((key,value)->{ //lambada
System.out.println(value);
});

2、


HashMap 常用的成员方法:

  1. **put(K key, V value)**:将键值对添加到 HashMap 中。如果键已存在,则新的值会替代旧值。
  2. **get(Object key)**:根据键获取对应的值。
  3. **remove(Object key)**:根据键删除键值对。
  4. **containsKey(Object key)**:检查是否存在指定键。
  5. **containsValue(Object value)**:检查是否存在指定值。
  6. **size()**:返回 HashMap 中键值对的数量。
  7. **isEmpty()**:检查 HashMap 是否为空。
  8. **clear()**:清空 HashMap 中的所有键值对。
  9. **keySet()**:返回包含 HashMap 所有键的集合。
  10. **values()**:返回包含 HashMap 所有值的集合。
  11. **entrySet()**:返回包含 HashMap 所有键值对的集合。
  12. **putAll(Map<? extends K, ? extends V> m)**:将另一个映射的键值对添加到当前 HashMap 中。
  13. **replace(K key, V newValue)**:替换键对应的值。
  14. **getOrDefault(Object key, V defaultValue)**:根据键获取对应的值,如果键不存在,则返回默认值。
  15. **computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)**:根据键计算值,如果键不存在,则执行提供的计算函数。
  16. **computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)**:根据键计算值,如果键存在,则执行提供的计算函数。
  17. **forEach(BiConsumer<? super K,? super V> action)**:对每个键值对执行指定操作。


HashTable

和HashMap一样,Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射。

Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。

Hashtable 的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。此外,Hashtable中的映射不是有序的。

默认加载因子是 0.75

Hashtable的构造函数

// 默认构造函数。
public Hashtable()

// 指定“容量大小”的构造函数
public Hashtable(int initialCapacity)

// 指定“容量大小”和“加载因子”的构造函数
public Hashtable(int initialCapacity, float loadFactor)

// 包含“子Map”的构造函数
public Hashtable(Map<? extends K, ? extends V> t)

Hashtable的API

synchronized void                clear()
synchronized Object clone()
boolean contains(Object value)
synchronized boolean containsKey(Object key)
synchronized boolean containsValue(Object value)
synchronized Enumeration<V> elements()
synchronized Set<Entry<K, V>> entrySet()
synchronized boolean equals(Object object)
synchronized V get(Object key)
synchronized int hashCode()
synchronized boolean isEmpty()
synchronized Set<K> keySet()
synchronized Enumeration<K> keys()
synchronized V put(K key, V value)
synchronized void putAll(Map<? extends K, ? extends V> map)
synchronized V remove(Object key)
synchronized int size()
synchronized String toString()
synchronized Collection<V> values()


LinkedHashSet

image-20231020172248026

LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("a","1");
linkedHashMap.put("b","2");
System.out.println(linkedHashMap);


TreeMap

image-20231020190435391

  • 比较规则
Map<String, String> map = new TreeMap<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
map.put("c","c");
map.put("a","a");

//比较规则:按键升序
Map<String, String> map = new TreeMap<>(Comparator.naturalOrder());





方法引用

方法引用的规则

1.需要有函数式接口

2.被引用的方法必须已经存在

3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致

4.被引用方法的功能需要满足当前的需求

image-20231007140435261

image-20231006231129619

image-20231006230903730



public class demo1 {
public static void main(String[] args) {
Integer arr[] = {1,9,2,7,6,3};

//Lambda写法
//Arrays.sort(arr, ( o1, o2) -> o1 - o2);

//表示引用demo1类里面的DiyCompare方法
//把这个方法当做抽象方法的方法体
Arrays.sort(arr,demo1::DiyCompare);
}

public static int DiyCompare(Integer a,Integer b){
return a-b;
}
}


引用静态方法

image-20231006235757736

  • · 换为 ::
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"1","2","3","4","5");

//匿名内部类
list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
}).forEach(System.out::println);

//1.方法需要已经存在
//2.方法的形参和返回值需要跟抽象方法的形参和返回值保持一致
//3.方法的功能需要把形参的字符串转换成整数

//使用方法引用
list.stream().map(Integer::parseInt).forEach(System.out::println);


引用成员方法

  • 引用处不能是静态方法
  • 其他类: new 类()::方法

image-20231006235825337


  • 其他类

public class StringOperation {
public boolean stringJudge(String s){
return s.startsWith("陈");
}
}
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"陈呀","陈辰","彭呀","彭辰","李呀");
//其他类:其他类对象::方法名
list.stream().filter(new StringOperation()::stringJudge).forEach(System.out::println);


引用构造方法

image-20231007010840062


  • 原写法
List<Student> collect = list.stream().map(new Function<String, Student>() {
@Override
public Student apply(String s) {
return new Student(s.split(",")[0], Integer.parseInt(s.split(",")[1]));
}
}).collect(Collectors.toList());

  • 使用方法引用
//bean
public class Student {

//添加方法
public Student(String s) {
this.name=s.split(",")[0];
this.age=Integer.parseInt(s.split(",")[1]);
}
}


//引用 Student::new
List<Student> collect = list.stream().map(Student::new).collect(Collectors.toList());





其他调用方式

image-20231007132651880

list.stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toUpperCase();
}
}).forEach(System.out::println);

//使用类名引用成员方法
//拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果
list.stream().map(String::toUpperCase).forEach(System.out::println);

  • 独有规则

1.需要有函数式接口

2.被引用的方法必须已经存在

3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。

4.被引用方法的功能需要满足当前的需求

抽象方法形参的详解

第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法

第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法

局限性

不能引用所有类中的成员方法。

是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。

image-20231007133928024



image-20231007140226777

Integer[] integers = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});


/**
* 方法引用(数组的构造方法)
* 格式: 数据类型[]::new
* 目的:创建一个指定类型的数组
*/
//数组的类型,需要跟流中数据的类型保持一致。
Integer[] integers = list.stream().toArray(Integer[]::new);


总结

image-20231007140326691



File

File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。


使用

image-20231007142023404

//1.根据文件路径创建文件对象
String str = "D:\\1.JavaRoute\\Java\\file.txt";
File file1 = new File(str);


//2.根据父路径名字符串和子路径名字符串创建文件对象
String parent = "D:\\1.JavaRoute\\Java";
String child = "file.txt";
File file2 = new File(parent, child);
File file3 = new File(parent + "\\" + child);


//3.把一个Fi1e表示的路径和String表示路径进行拼接
File parent3 = new File("D:\\1.JavaRoute\\Java");
String child2 = "file.txt";
File file4 = new File(parent3,child2);

image-20231007142059524



获取并遍历

image-20231007142223164

image-20231007150212343

File f = new File("D:\\1.JavaRoute\\Java");
File[] files = f.listFiles();
for (File file5 : files) {
System.out.println(file5);
}

image-20231007142348879



创建与删除

image-20231007143859451

/**
* 细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true
* 如果当前路径表示的文件是存在的,则创建失败,方法返回fa1se
* 细节2:如果父级路径是不存在的,那么方法会有异常I0 Exception
* 细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
*/
File file = new File("D:\\1.JavaRoute\\Java\\c.txt");
boolean newFile = file.createNewFile();





IO流

image-20231007152546935



分类

image-20231007152225224

image-20231007201409766



字节流

  • 每次只能操作一个字节

image-20231007153124196



输出流–写出

  • 写出的是单个字节数据 – 十进制
        /**
* 目标确保存在,文件不存在则自动创建
* 文件存在,清空文件,默认不是追加数据
*/
//输出流--写出
FileOutputStream fileOutputStream = new FileOutputStream("D:\\1.JavaRoute\\Java\\c.txt");
fileOutputStream.write(99); //哈希值
fileOutputStream.close();

image-20231007154900543

  • 同一个流多次写入为 :追加

image-20231007154729170

FileOutputStream fileOutputStream = new FileOutputStream("D:\\1.JavaRoute\\Java\\c.txt");
byte[] bytes = {97,98,99,100,101};
fileOutputStream.write(bytes,0,2); //数组,起始索引,个数
fileOutputStream.close(); //释放资源


//多文本输入 -- 转为 byte[] 即可
String str1 = "你好,ya";
byte[] bytes1 = str1.getBytes();
fileOutputStream.write(bytes1);

//同一流中,多次写出为追加
String wrap = "\r\n"; //换行
byte[] bytes2 = wrap.getBytes();
fileOutputStream.write(bytes2);

String str2 = "99999"; //换行
byte[] bytes3 = str2.getBytes();
fileOutputStream.write(bytes3);
  • 开启追加续写 – 创建对象的第二个参数–默认false

FileOutputStream fileOutputStream = new FileOutputStream("D:\\1.JavaRoute\\Java\\c.txt",true);

image-20231007162054013



输入流–读入

  • 读取之后,read.方法的底层还会进行将字节进行解码并转成十进制

image-20231007170044311

image-20231007164157514


//输入流--读取
FileInputStream fileInputStream = new FileInputStream("D:\\1.JavaRoute\\Java\\c.txt");
//读取单个数据--底层迭代器,指针移动,空则返回-1
int read = fileInputStream.read();
//关闭
fileInputStream.close();



//--------------------循环读取--------------------
/**
* 读取单个数据--底层迭代器,指针移动
* read:读取并移动
*/
int read ;
while ((read = fileInputStream.read()) != -1) {
System.out.print((char) read);
}

//--------------------一次读取多个数据--------------------
byte[] bytes = new byte[10];
int read = fileInputStream.read(bytes);
//(数组,下标,长度) 如果不设置长度,当read为-1空的时候,bytes不会被覆盖,造成数据残留
String s = new String(bytes,0,read);


文件拷贝

  • 单个文件
//输入流
FileInputStream fileInputStream = new FileInputStream("D:\\1.JavaRoute\\Java\\copy.mp4");
//输出流
FileOutputStream fileOutputStream = new FileOutputStream("D:\\1.JavaRoute\\Java\\Test\\copy.mp4");

int read;
long start = System.currentTimeMillis();

//一次读取一个字节
while ((read = fileInputStream.read())!=-1){
fileOutputStream.write(read);
}

//一次读取多个数据
byte[] bytes = new byte[1024*1024*5];
while ((read = fileInputStream.read(bytes))!=-1){
fileOutputStream.write(bytes);
}

long end = System.currentTimeMillis();

//关闭
fileOutputStream.close();
fileInputStream.close();
System.out.println("copy时间:"+(end-start));

  • 考贝一个文件夹,考虑子文件夹
public class TestCopy {
public static void main(String[] args) throws IOException {

//考贝一个文件夹,考虑子文件夹

File fileInput = new File("D:\\1.JavaRoute\\Java\\Test");

File fileOutput = new File("D:\\1.JavaRoute\\Java\\TestC");

copyTo(fileInput,fileOutput);

}

private static void copyTo(File fileInput, File fileOutput) throws IOException {
File[] files = fileInput.listFiles();
for (File file : files) {
if (file.isDirectory()){
System.out.println("是文件夹");
File file1 = new File(fileOutput,file.getName());
file1.mkdir();
copyTo(file,new File(fileOutput,file.getName()));
}else if (file.isFile()){
System.out.println("是文件");
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutput,file.getName()));
int read;
while ((read = fileInputStream.read())!=-1){
fileOutputStream.write(read);
}
fileOutputStream.close();
fileInputStream.close();
}
}

}
}

  • 加密与解密
//加密与解密
/**
* 写入使用异或 ^ 因为数据异或两次等于原来的数据,所以加密异或一次,解密异或一次
*/

File fileInput = new File("D:\\1.JavaRoute\\Java\\Test\\IO\\cc.jpg");

File fileOutput = new File("D:\\1.JavaRoute\\Java\\Test\\IO\\1");

FileInputStream fileInputStream = new FileInputStream(fileInput);
FileOutputStream fileOutputStream = new FileOutputStream(new File(fileOutput,fileInput.getName()));

int read;
while ((read=fileInputStream.read())!=-1){
fileOutputStream.write(read ^ 10);
}

fileOutputStream.close();
fileInputStream.close();


字符集

image-20231007182717971

img


UTF-8Unicode 字符集的一种编码方式

image-20231007183833292



编码与解码

image-20231007184327948






字符流

  • 相比字节流,字符流存在缓冲区,如果不手动刷新或者关闭流,数据不会存储到文件中

image-20231007184858793

image-20231007185000227



FileReader–读入

  • 空参read方法

FileReader fileReader = new FileReader("D:\\1.JavaRoute\\Java\\c.txt");
int read;
//读取之后,read.方法的底层还会进行将字节进行解码并转成十进制
/**
* 最终把这个十进制作为返回值
* 这个十进制的数据也表示在字符集上的数字
* 英文:文件里面二进制数据61100601 read方法进行读取,解码并转成十进制97
* 中文:文件里面的二进制数据111081101911090110001001 read方法进行读取,解码并转成十进制27721
* 强转即可显示原内容
*/
while ((read=fileReader.read())!=-1){
System.out.print((char) read);
}

  • 带参read方法

一次性读取多个数据 使用 char数组

//read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
char[] chars = new char[2];
while ((read=fileReader.read(chars))!=-1){
System.out.println(new String(chars,0,2));
}


FileWrite–写出

  • 只能字符,数字的话默认为10进制后后转为二进制进行编码

image-20231007191222824

image-20231007191908547

File file = new File("D:\\1.JavaRoute\\Java\\file.txt");
FileWriter fileWriter = new FileWriter(file,true); //追加
fileWriter.write(25105);
fileWriter.write("chenya");
char[] chars={'a','b','c','d','e'};
fileWriter.write(chars);
fileWriter.close();

/**
* 排序后更新内容
* 2-1-9-4-7-8
* 1-2-4-7-8-9
*/

FileReader fileReader = new FileReader("D:\\1.JavaRoute\\Java\\Test\\c.txt");
int read;
StringBuilder stringBuilder = new StringBuilder();
while ((read=fileReader.read())!=-1){
stringBuilder.append((char) read);
}

String[] split = stringBuilder.toString().split("-");
Integer[] integers = Arrays.stream(split).map(Integer::parseInt).sorted().toArray(Integer[]::new);
String toString = Arrays.toString(integers).replace(", ", "-");
String result = toString.substring(1,toString.length()-1);

FileWriter fileWriter = new FileWriter("D:\\1.JavaRoute\\Java\\Test\\c.txt");
fileWriter.write(result);

fileWriter.close();
fileReader.close();


缓冲区

  • 输入流

存在缓冲区(大小:8192):一次性读取全部字节存入内存,再从内容读取

一次性读取一个字节,如果字节符合中文编码(负数),则一次性读取3个字节,并进行解码为10进制对应的字符,如果想显示对应的数据,需要强转为字符(char)

image-20231007194732579


  • 输出流

image-20231007195811090






打印流

  • 只能写出,不能读入

    image-20231008005456070

使用场景:System.out.print()

//特殊的打印流,系统中的标准输出流,是不能洐闭,在系统中是唯一的。
PrintStream out = System.out;
out.printf("此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台");

字节打印流

  • 原样写出:写入什么数据就写入什么数据,不转为二进制查询ASCII码表

image-20231008002639573

image-20231008002753229

PrintStream printStream = new PrintStream(new FileOutputStream("D:\\1.JavaRoute\\Java\\Test\\print.txt",true),true, "UTF-8");
//写出
printStream.println(992);//写出+自动刷新+自动换行
printStream.println(true);
printStream.printf("%s爱上了%s","辰","陈"); //占位符
//释放
printStream.close();


字符打印流

image-20231008004131293

image-20231008002753229

PrintWriter printWriter =  new PrintWriter(new FileWriter("D:\\1.JavaRoute\\Java\\Test\\print.txt",true),true);
printWriter.println("yes");
printWriter.close();





序列化

序列化流

  • 把对象写出到文件

image-20231007215457921

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\1.JavaRoute\\Java\\Test\\Student.txt"));

//单个写入
Student student = new Student("辰呀", 18);
objectOutputStream.writeObject(student);

//多个写入 -- list
Student student1 = new Student("辰呀", 18);
Student student2 = new Student("辰", 18);
ArrayList<Student> list = new ArrayList<>();
list.add(student1);
list.add(student2);
objectOutputStream.writeObject(list);

objectOutputStream.close();

对象需要实现 序列化接口

/**
* Serializable接口里面是没有抽象方法,标记型接口
* 一且实现了这个接口,那么就表示当前的Student类可以被序列化
*/
public class Student implements Serializable {
}


反序列化流

  • 把存储到文件中的对象反序列化成java对象

image-20231007220247602

//反序列化流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\1.JavaRoute\\Java\\Test\\Student.txt"));

//读取数据 - 一次只能读取一个对象
Student student1= (Student) objectInputStream.readObject();

//读取数据 - 多个对象 - list
ArrayList<Student> list = (ArrayList<Student>) objectInputStream.readObject();
for (Student student : list) {
System.out.println(student);
}

//关闭
objectInputStream.close();

序列化流和反序列化流的常见问题:

①版本号

  • 当序列化存储到文件中后 对象构造发生变化 反序列化回来会报错
private static final long serialVersionUID = 版本号L;

②指定属性不被序列化,反序列化得到的是null

//transient:瞬态关键字,属性防止序列化,
private transient String address;

image-20231007222057372