java第二阶段

枚举类package chaper11.iml.enum_;

enumeration简写enum

枚举是一组常量的集合,是一种特殊的类,只包含一组有限的特定的对象

自定义枚举类

1.构造器私有化

2.本类创建一组特定的对象

3.对外暴露对象,使用public static final修饰

4.提供get方法,但不提供set方法

enum关键字来实现枚

1.当我们使用enum关键字来开发一个枚举类时,会默认继承Enum类,而且enum修饰的类是一个final类

2.public static final season SPRING = new season("春天","温暖");变为SPRING("春天","温暖");

3.如果使用的是无参构造器,创建常量对象则可以省略小括号,不传递进参数

4.多个枚举对象时用,分隔,但最后使用;结尾

5.枚举对象必须位于行首

Enum的String方法是返回常量对象的名字

enum常用方法说明

由于枚举类继承了Enum类,故可以使用Enum的一些方法

package chaper11.iml.enum_;

/**
 * @author 刘宇
 */
public class EnumMethod {
    public static void main(String[] args) {
        //以season类来演示
        season autumn = season.AUTUMN;
        System.out.println(autumn);//
        System.out.println(autumn.name());//输出枚举对象的名字
        //ordinal()方法用以输出该枚举对象的编号,从0开始编号
        System.out.println(autumn.ordinal());//autumn是第三个,故输出2
        //从反编译可以看到values方法,返回一个数组,含有定义的所有枚举对象
        season[] values = season.values();
        for (season s : values) {//增强for循环,取values数组 赋给s,取出完毕退出for
            System.out.println(s);
        }
        //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则异常
        //根据输入的"AUTUMN"到season中的枚举对象中去查找,找到了就返回没找到就报错
        season autumn1 = season.valueOf("AUTUMN");
        System.out.println("autumn1=" + autumn1);
        System.out.println(autumn1==autumn);
        //compareTo比较两个枚举常量的位置号的差,即AUTUMN-SPRING
        System.out.println(season.AUTUMN.compareTo(season.SPRING));1
    }
}

enum实现接口

1.使用enum关键字后,就不能继承其他类了,因为enum会隐式继承Enum类,java是单继承机制

2.enum可以实现接口

注解(Annotation)

使用Annotation时前面要加@符号,并把该Annotation当成一个修饰符使用.用于修饰他支持的程序元素

三个基本的Annotation

1.@override: 限定某个方法,是重写父类方法,该注解1只能用于方法

2.@Deprecated:用于表示某个程序元素(类,方法)已过时

3.@SuppressWarnings:抑制编译器警告

Override注解

@Override

重写的方法

如果加上Override注解,编译器会去检查该方法是否真的重写了父类的方法,如果重写了,则编译通过,负责则不通过

如果发现@interface,则表示一个注解类

@Target是修饰注解的注解,称为元注解

Deprecated注解

@Deprecated修饰某个元素,表示该元素已经过时,即不推荐使用,但还可以用.

作用是可以做到新旧版本的兼容和过度

SuppressWarnings注解

@SuppressWarnings 是 Java 提供的一个注解(Annotation),用于告诉编译器忽略特定的警告信息。这个注解可以应用于类、接口、方法或字段上,以抑制编译器在编译期间生成的特定警告。这在你确信某些警告是无关紧要的或者在你无法立即修复它们但又不想看到它们时特别有用。

@SuppressWarnings 注解后面需要跟一个或多个字符串参数,这些字符串指定了要抑制的警告类型。这些类型通常是编译器警告的关键字,如 uncheckeddeprecationrawtypes 等。如果需要抑制多种类型的警告,可以使用逗号分隔它们。

四种元注解(了解)

1.Retention//指定注解的作用范围,源码,类,运行时

2.Target//指定注解可以在哪些地方使用

3.Documented//指定该注解是否会在javadoc体现

4.Inherited//子类会继承父类注解

异常package com.study.exception_;

介绍

程序执行中发生的不正常的情况称为"异常",逻辑错误或语法错误不是异常

可分为两大类

Error(错误):Java虚拟机无法解决的严重问题.如jvm系统内部错误,资源耗尽等严重情况.比如:StackOverflowError[栈溢出],OOM(out of memory),Error是严重错误,程序会崩溃

Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理.例如空指针访问,试图读取不存在的文件,网络连接中断等等,Exception分为两大类:运行时异常编译时异常

异常体系图

体现了继承和实现关系,虚线实现,实线继承

编辑

1.运行分为两大类,运行时异常和编译时异常.

2.运行时异常,编译器检测不出来.一般是值编程时的逻辑错误,是程序员应该避免出现的异常

3.对于运行时异常可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响

4.编译时异常是编译器要求必须处理的异常

常见运行时异常

NullPointerException空指针异常

public class NullPointException_ {
    public static void main(String[] args) {
        String name = null;
        System.out.println(name.length());
    }
}

ArithmeticException数字运算异常

比如除数为0时

ArrayIndexOutOfBoundsException数组下标越界异常

public class ArrayIndexOutOfBoundsException_ {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5};
        for(int i=0;i<=arr.length;i++){
            System.out.println(arr[i]);
            ArrayIndexOutOfBoundsException
        }
    }
}

ClassCastException类型转换异常

public class ClassCastException_ {
    public static void main(String[] args) {
      A b = new B();//向上转型
      B b2 = (B) b;//向下转型
        C c2 = (C)b;//这里抛出ClassCastException
    }
}

class A{}
class B extends A{}
class C extends A{}

NumberFormatException数字格式不正确

public class NumberFormatException_ {
    public static void main(String[] args) {
        String name = "1234";
        String name1 = "小罗";
        int num = Integer.parseInt(name);
        int num1 = Integer.parseInt(name1);//NumberFormatException
        System.out.println(num);
        System.out.println(num1);
    }

编译时异常

编辑

异常处理机制

try-catch

try{

代码可能有异常

包含可能出错的代码

}catch(Exception e){

//1.捕获到异常

//2.系统将异常封装给Exception对象e,传递给catch

//3.得到异常对象后,程序员需自己处理

//4.注意,如果代码块没有发生异常,catch代码块就不执行

}finally{

//1.不管try代码块是否有异常发生,始终要执行finally

//2.所有,通常将释放资源的代码放在finally里面

}

finally可写可不写语法没问题

try-catch细节

ctrl+alt+t快速括起觉得会出问题的代码选6是try-catch

1.如果异常发生了,则异常发生后面的代码不会在运行

2.如果没有异常,则顺序执行try的代码,不会进入catch

3.如果希望不管是否发生异常,都执行某段代码(比如关闭连接 ,释放资源等)则可使用finally

4.可以有多个catch语句,捕获不同的异常(进行不同的业务处理),但要求父类异常在后,子类异常在前.

5.可以进行try-finally配合使用,但这种用法相当于没有捕获异常,因此程序会直接崩掉/退出.

6.try中遇到return或抛出异常这种语句,就会直接跳到finally,执行,最后才运行这个语句后的语句,除非在finally也有return,就会提前跳出

try-catch-finally小结

1.如果没有异常,则顺序执行try的代码,不会进入catch,最后还需要执行finally里面的语句

2.如果出现异常,则Try块中异常发生后,剩下的语句不再执行,将执行catch中的语句,最后还需要执行finally里面的语句

package com.study.Homework01;

/**
 * @author 刘宇
 */
public class TryCatchExercise02 {
    public static int method(){
        int i=1;
        try{
            i++;
            String[] names = new String[3];
            if(names[1].equals("tom")){//names[1]是空指针,会包空指针异常
                System.out.println(names[1]);
            }else{
                names[3] = "hspedu";
            }
            return 1;
        }catch(ArrayIndexOutOfBoundsException e){
            return 2;
        }catch(NullPointerException e){//捕获,但由于finally必须执行
            return ++i;//此处要返回,但由于还要执行finally的方法
            //会创建一个临时变量用于保存当前i的值,如果finally中无返回语句,就会返回这个临时变量
        }finally{
            ++i;
            System.out.println("i="+i);
        }
    }
    public static void main(String[] args) {
        System.out.println(method());
    }
}
try-catch最佳实践

利用异常处理机制完成强制用户输入一个整数

package com.study.Homework01;

import java.util.Scanner;

/**
 * @author 刘宇
 * 利用Try-catch异常处理机制强制用户输入一个整数
 */
public class TryCatchExercise03 {
    public static void method() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个整数:");
        int num = 0;
        while(true){
            try {
                num = Integer.parseInt(sc.next());
                //Integer.parseInt() 是 Java 中的一个静态方法,它属于 java.lang.Integer 类。
                // 这个方法用于将字符串参数解析为十进制整数。
                //如果字符串包含的不是有效的整数表示,
                // 则该方法会抛出一个 NumberFormatException。
                System.out.println("你输入的整数是"+num);
            } catch (NumberFormatException e) {
                System.out.println("你输入的不是整数,请重新输入");
                method();
            }
        }
    }

    public static void main(String[] args) {
        method();
    }
}

throws

处理机制图

将发生的异常抛出,交给调用者(方法)来处理最顶级的处理者是JVM

编辑

1.try-catch-finally和throws二选一

2.如果程序员没有显式处理异常,则默认采用throws

throws异常处理

1.对于编译异常,程序中必须处理,比如try-catch和throws

2.对于运行时异常,程序中如果没有处理,默认就是throws的方法处理

3.子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出异常类型要么和父类抛出的异常一致,要么为父类抛出异常的子类@异常体系图

4.在throws过程中,如果有方法try-catch.就相当于处理异常,就可以不必再throws,否则一致抛出直到遇到try-catch或抛到JVM机中

package com.study.exception_;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

/**
 * @author 刘宇
 */
public class Throws02 {
    public static void main(String[] args) {

    }

    public static void f1() throws Exception {
        f2();//此处需要处理异常或继续抛出异常
    }

    public static void f2() throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("d://aa.txt");
        //创建一个文件流对象
    }
    public static void f3() throws ArithmeticException{
        int num =10;
        int n=0;
        System.out.println(num/n);
    }
    public static void f4(){
        //1.可以调用f3()方法,原因是f3()方法抛出的是运行异常
        //2.而Java中,并不要求程序员显示处理运行异常,因为有默认处理机制
        f3();
    }
}

class Father{
    public void method() throws RuntimeException{
    }
}
class Son extends Father{
    @Override
    public void method() throws ArithmeticException{//子类重写异常方法时
        //所抛出的异常要么和父类一致,要么是父类抛出异常的子类型
    }
}

自定义异常package com.study.customexception;

步骤

1.定义类:自定义异常类名,继承Exception或RuntimeException

2.如果继承Exception属于编译异常

3.如果继承RuntimeException属于运行异常

举例

package com.study.customexception;

/**
 * @author 刘宇
 */
public class CustomException {
    public static void main(String[] args) {
        int age=80;
        if(!(age>=18&&age<=100)){
            //这里创建一个AgeException类对象并调用父类
            //RuntimeException的构造器设置信息
            throw new AgeException("年龄必须在18-100岁间")
        }
        System.out.println("您的年龄范围正确!");
    }
}
//自定义异常
//1.一般情况下,我们自定义异常是RuntimeException
//2.即把自定义异常定为运行时异常,好处是,我们可以使用默认的处理机制
class AgeException extends RuntimeException {
    public AgeException(String message) {
        super(message);//ctrl+左键追看父类构造器
    }
}

throw和throws的区别

throws异常处理的一种方式 在方法声明处 后面跟异常类型

throw手动生成异常对象的关键字 在方法体中 后面跟异常对象

例题

编辑

分析:
1.从主方法开始运行,遇到try,进入,遇到ReturnExceptionDemo.methodA()方法,跳到方法A,输出语句1
2.遇到throw关键字,会提前跳到finally,输出2
3.回到该语句,已经抛出异常语句,回到主方法,被主方法中的catch捕获,e.getMessage()输出语句跑过来的语句
4.运行到R...B语句,进入方法B,输出3
5.遇到return跳到finally输出5回到return 
6.退出

常用类package com.study.WrapperType;

包装类

1.针对八种基本数据类型相应的引用类型-包装类

2.有了类的特点就可以调用类中的方法

对应关系如图

编辑

包装类和基本数据类型相互转换

以int 和integer演示

装箱:基本类型->包装类型反之拆箱

package com.study.WrapperType;

/**
 * @author 刘宇
 */
public class Integer01 {
    public static void main(String[] args) {
        //int<-->Integer
        //jdk5以前是手动装箱
        int n1 = 100;
        Integer integer = new Integer(n1);
        Integer integer1 = Integer.valueOf(n1);
        //手动拆箱
        int i = integer.intValue();

        //jdk5后就可以自动装箱和自动拆箱了
        int n2 = 200;
        //自动装箱
        Integer integer2 = n2;//底层使用的是Integer.valueOf(n2)
        //自动拆箱
        int n3 = integer2;//底层仍使用的是intValue()方法
    }
}

包装类方法

包装类型和String类型的相互转换

package com.study.WrapperType;

/**
 * @author 刘宇
 */
public class WrapperVSString {
    public static void main(String[] args) {
        //包装类->String
        Integer i = 100;//自动装箱
        //方式1
        String str = i + "";//对原本i的类型无影响
        //方式2
        String str2 = i.toString();
        //方式3
        String str3 = String.valueOf(i);//

        //String->包装类
        String str4="12345";
        //方式1
        Integer i2 = Integer.parseInt(str4);//返回int,使用了自动装箱
        //方式2
        Integer i3 = new Integer(str4);
    }
}

常用的方法

编辑

Integer创建机制

范围-128-127

只要有基本数据类型,判断的是值是否相等

package com.study.WrapperType;

/**
 * @author 刘宇
 */
public class Integer02 {
    public static void main(String[] args) {
        method();
    }

    public static void method() {
        Integer i = new Integer(1);
        Integer j = new Integer(1);
        System.out.println(i == j);//不是同一个对象F
        //看源码
        /*public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
         */
        Integer m = 1;//Integer.valueOf(1);
        Integer n = 1;
        System.out.println(m == n);//T
        //主要看范围,-128-127就是直接返回,因为静态方法只会加载一次
        // 否则就会new Integer(xx)
        Integer x = 128;
        Integer y = 128;
        System.out.println(x == y);//f
    }
}

Integer面试题

只要有基本数据类型,判断的是值是否相等

编辑

String类

简单介绍

//String 对象用于保存字符串,即一组字符序列,即里面有个value[]字符数组
//"abc"是字符串常量,双括号括起的字符序列
//字符串的字符使用Unicode编码,一个字符(不区分字母还是汉字),占2个字节
//String类有很多的构造器
//String实现了接口Serializable,从而String可以串行化实现网络传输
//接口Comparable[String对象可以比较大小
//String类时final类
//String有属性private final char value[];用于存放字符串内容
//一定要注意:value是一个final类,不可以修改value的地址,即指向内存空间的地址无法修改

String创建剖析

方式一:直接赋值 String s = "lans";

方式二:调用构造器 String s2 = new String("lans");

解读:

1.方式一:先从常量池看看是否有"lans"数据空间,如果有,直接指向;如果没有则重新创建,然后指向,s最终指向的是常量池的空间地址

2.方式二:现在堆中创建空间,里面维护了value属性,指向常量池的"lans"空间.如果没有则重新创建,如果有,直接通过value指向.最终指向的是堆中的空间地址

new出来和赋值不同!

注:String类中的equals方法已被重写,是按顺序一个一个比较字符

例题

package com.study.String_;

/**
 * @author 刘宇
 */
public class String02 {
    public static void main(String[] args) {
        Person p1 = new Person();
        //此处相当于直接赋了一个常量给p1.name,p1->堆中的name->lansss
        p1.name = "lansss";//赋值就直接指向常量池,new就指向堆
        Person p2 = new Person();
        p2.name = "lansss";
        //        同理p2->name->lansss同一个对象
        System.out.println(p1.name.equals(p2.name));//T
        System.out.println(p1.name == p2.name);//T
        System.out.println(p1.name == "lansss");//T,还是同一个对象
        String s1 = new String("abcd");
        String s2 = new String("abcd");
        System.out.println(s1 == s2);
        //F 因为是new出来的,s1,s2指向的不是同一个value,比较的为s1s2返回F


        String a = "ly";
        String b = new String("ly");
        System.out.println(a == b);//new了即b指向的地址是堆中指向常量池的地址,两地址不一样返回F
        System.out.println(a.equals(b));//equals方法比较字符的顺序是否完全相同,返回T
        //当调用intern方法时,如果常量池已经有对象b对应的字符串(通过equals确认)
        // 就会直接返回字符串,否则就会添加此字符串后返回
        System.out.println(a == b.intern());//a b.intern()都是一个地址的字符串,返回true
        System.out.println(b == b.intern());//b指向一个堆中的空间再执行字符串,返回false
    }
}

class Person {
    String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }
}

String对象特性

1.String是一个final类,代表不可变的字符序列

2.字符串是不可变的.一个字符串对象一旦被分配,其内容是不可变的

总结

1.Java中String对象的不可变性

String 对象的不可变性意味着一旦 String 对象被创建,那么存储在其中的字符序列就不能被改变。任何对 String 对象的修改实际上都会创建一个新的 String 对象。

2.final 修饰的数组引用不可变(但数组内的元素可以改变)

final 修饰的数组引用意味着一旦数组被赋值给一个 final 变量,那么这个变量就不能再指向另一个数组。但是,数组内的元素是可以被修改的(但数组的元素是可变的,比如 char[]int[] 等)。

3.String c1 = "ab"+"cd";常量相加,看的是常量池;String c = a + b;变量相加,是在堆中

在这个例子中,"ab" + "cd" 是在编译时就确定的,编译器会优化这个表达式,直接在字符串常量池中查找(或创建)"abcd" 字符串,并将 c1 引用指向它。这里不会发生运行时的字符串连接操作,因此不会在堆上创建新的 String 对象(除非字符串常量池中没有找到该字符串)。

在这个例子中,ab 是变量,它们在编译时并不知道它们将包含什么值。因此,a + b 的操作只能在运行时进行。这意呀着JVM将不得不创建一个新的 String 对象来存储连接后的结果("ab"),并且这个新对象会被创建在堆上。这个新对象不会放入字符串常量池,除非显式地调用 intern() 方法。

理解加深

package com.study.String_;

/**
 * @author 刘宇
 */
public class StringExercise {
    public static void main(String[] args) {
        Text1 ex = new Text1();
        ex.change(ex.str,ex.ch);
        System.out.print(ex.str+" and ");
        System.out.println(ex.ch);
    }
}
class Text1{
    String str = new String("lans");
    final char[] ch = {'h', 'e', 'l', 'l', 'o'};
    public void change(String str,char ch[]){
        str = "hello";
        ch[0] = 'j';
    }
}
解读:
//在change方法中,尝试修改传入的字符串的值,和字符数组的元素,字符串和数组都是引用类型
//但由于String对象的不可变性,任何类似改变字符串的操作都会让这个对象指向一个新的字符串对象
//且此处由于形参和实参同名,形参会对实参造成一种遮蔽(即操作str实际操作的是局部变量str)
//如果没重名,就可以分别对形参和实参进行操作.
//数组同样是引用类型,局部变量ch和全局变量ch都指向同一个数组对象,可以通过操作ch修改数组中的元素
//但由于final的修饰,无法让他指向另一个数组,即让ch = {'a','b','g'}会编译错误

String类的常见方法

package com.study.String_;

/**
 * @author 刘宇
 */
public class StringMethod {
    public static void main(String[] args) {
        //equals区分大小写
        String str1 = "Hello World";
        String str2 = "Hello WorlD";
        System.out.println(str1.equals(str2));
        //equalsIgnoreCase忽略大小写判断内容是否相同
        System.out.println(str1.equalsIgnoreCase(str2));
        //.length返回字符串的长度
        System.out.println(str1.length());
        //indexOf获取字符在字符串对象中第一次出现的索引,从0开始
        System.out.println(str2.indexOf('l'));//返回2,没有要找的字符返回-1
        System.out.println(str2.indexOf("ll"));//找相应的字符串,返回第一个字符的位置
        //lastIndexOf获取字符在字符串对象中最后以此出现的索引,从0开始,找不到同样返回-1
        System.out.println(str2.lastIndexOf('l'));
        //substring截取指定范围的子串
        System.out.println(str2.substring(6));//从索引6截取到后面所有的内容
        System.out.println(str2.substring(0,6));//截取从0开始到6-1=5位置,

    }
}
package com.study.String_;

/**
 * @author 刘宇
 */
public class StringMethod02 {
    public static void main(String[] args) {
        String str = "Hello World";
        //toUpperCase全变大写
        System.out.println(str.toUpperCase());
        //toLowerCase全变小写
        System.out.println(str.toLowerCase());
        //concat拼接字符串
        String str2 = str.concat(" World").concat("lans");
        System.out.println(str2);
        //replace替换字符串中的字符
        String s1 = "林黛玉 罗婧萱 张有为 ";
        s1 = s1.replace("林黛玉", "小罗");//将所有的林黛玉替换成小罗
        //本身对s1无影响,除非用他自己接收
        String s2 = s1.replace("罗", "宇");
        System.out.println(s1);
        System.out.println(s2);
        //分割字符串split,以()里面为标准
        //返回一个数组,以特殊字符为分隔符的时候,需要用到转义字符/
        String poem = "悟已往之不谏,知来者之可追";
        String[] split = poem.split(",");//以逗号为标准
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);//悟已往之不谏
//            知来者之可追
        }
//      toCharArray转移成字符数组
        String s3 = "happy";
        char[] chs = s3.toCharArray();
        for (int i = 0; i < chs.length; i++) {
            System.out.print(chs[i] + " ");
        }
        //compareTo比较两个字符串的大小,如果前者大返回整数,后者大返回负数,
        // 每个字符都相同,长度相同=返回0
        //如果长度相同或不同,但进行比较时可以区分大小写,就返回两个不同字符的差
        //如果前半部分相同,如Jac和Jack,就返回a.length()-b.length()
        String a = "cdkbt";
        String b = "cdkao";
        System.out.println("\n" + a.compareTo(b));//返回't'-'o'=5
        //格式化字符串format
        /*
        占位符有:
        %s字符串 %c字符 %d整形 %f浮点型
        %.3保留小数点三位
        同C语言 */
        String name = "lans";
        char gender = 'm';
        int age = 18;
        double score = 99.8;
        float grade = 1.2f;
        String formatstr = "我的姓名是%s 年龄是%d 性别是%c 总平是%.2f";
        String info = String.format(formatstr,name,age,gender,grade);
        System.out.println(info);
    }
}

StringBuffer类*

结构剖析

1.直接父类是AbstractStringBuilder

2.实现了Serializable,可以串行化,进行网络传输

3.在父类AbstractStringBuilder有属性char[] value,不是final

该value数组存放 字符串内容,引出存放在堆中

4.StringBuffer是一个final类不能被继承

StringBuffer Vs String

1.String保存的是字符串常量,里面的值不能修改,每次String类的更新实际上就是更改地址,效率低,因为有final

2.StringBuffer保存的是字符串变量,里面的值可以修改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高,空间大小不够才会更新.char[] value放在堆中,效率高

StringBuffer的构造器

1.StringBuffer();创建一个大小为16字符的char[]

2.StringBuffer(20);通过构造器指定char[]的大小

3.StringBuffer("lans");创建一个大小为str.length()+16

StringBuffer和String的相互转换

//String->StringBuffer
String str = "hello lans";
//方式一:使用构造器,返回的才是StringBuffer对象,对str本身无影响
StringBuffer sb = new StringBuffer(str);
//方式二:使用append方法
StringBuffer stringBuffer = new StringBuffer();
stringBuffer = stringBuffer.append(str);

//看StringBuffer->String
//使用提供的toString方法
StringBuffer stringBuffer3 = new StringBuffer("lansss");
String s = stringBuffer3.toString();
//使用构造器
String s1 = new String(stringBuffer3);

StringBuffer方法

package com.study.String_;

/**
 * @author 刘宇
 */
public class StringBuffer11 {

    public static void main(String[] args) {
        StringBuffer s = new StringBuffer("hello");
        //增
        s.append(',');
        s.append("world");
        System.out.println(s);//本质是调s.toString(),hello,world
        //删
        /*
        删除索引为>=start&&<end处的字符
         */
        //s.delete(1,3);
        System.out.println(s);//hlo,world,体现处StringBuffer不会随时更新地址
        //改
        //使用 好有味替换索引为9-11的字符[9,11)
        s.replace(1,3,"好有味");//h好有味lo,world
        System.out.println(s);
        //查,找不到返回-1
        int indexOf = s.indexOf("有味");
        System.out.println(indexOf);//2
        //插
        //在索引为3的位置插入"小罗",原来索引为3的自动后移
        s.insert(3,"小罗");
        System.out.println(s);//h好有小罗味lo,world
    }
}

StringBuilder类*

1)一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuffer不是线程安全).该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可以优先采用此类,因为大多数实现中,它更快

2).在StringBuilder上主要操作append和insert方法,可重载这些方法,以接受任意类型的数据

3)1.直接父类是AbstractStringBuilder

2.实现了Serializable,可以串行化,进行网络传输

3.在父类AbstractStringBuilder有属性char[] value,不是final

该value数组存放 字符串内容,引出存放在堆中

4.StringBuilder是一个final类不能被继承

StringBuilder应用

String StringBuffer StringBuilder比较

1.StringBuffer和StringBuilder非常类似,都代表可变的字符序列,而且方法一样.

2.String:不可变字符序列,效率低但复用率高

3.StringBuffer:可变字符序列,效率较高(增删),线程安全

4.StringBuilder:可变字符序列,效率最高,线程不安全

结论

1.如果字符串存在大量修改操作,一般使用StringBuffer或StringBuilder

2.如果字符串存在大量修改操作,并且单线程,使用StringBuilder

3.如果字符串存在大量修改操作,多线程,使用StringBuffer

4.如果我们字符串很少修改,被多个对象引用,使用String配置信息

Math类

package com.study.Math;

/**
 * @author 刘宇
 */
public class MathMethod {
    public static void main(String[] args) {
        //1.abs()求绝对值
        int abs = Math.abs(-10);
        System.out.println(abs);
        //2.pow求幂
        double pow = Math.pow(3,2 );//3的2次方
        System.out.println(pow);
        //3.ceil向上取整,返回>=该参数的最小整数
        double ceil = Math.ceil(9.4);//10.0
        System.out.println(ceil);
        //4.floor向下取整,返回<=该参数的最大整数
        double floor = Math.floor(9.4);//9.0
        System.out.println(floor);
        //5.round 四舍五入
        long round = Math.round(9.4);//9,int不行,精度太小
        double round2 = Math.round(9.5);//10.0
        System.out.println(round);
        System.out.println(round2);
        //6sqrt开方,负数返回..
        double sqrt = Math.sqrt(9);//3.0
        System.out.println(sqrt);
        //7random返回一个随机数,返回[0,1)之间的随机小数
        //        for(int i = 0;i<10;i++){
        //            System.out.println(Math.random());
        //        }
        //想获取随机整数,公式:获取a-b的一个随机整数
        //int num = (int)(a+Math.random()*(b-a+1))
        for(int i = 0;i<5;i++){
            int num = (int)(2+Math.random()*(7-2+1));
            System.out.println(num);
        }
        //max,min返回最大最小值
        System.out.println(Math.max(3,6));
        System.out.println(Math.min(3,6));
    }
}

Arrays类

package com.study.Arrays;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @author 刘宇
 */
public class ArraysMethod {
    public static void main(String[] args) {
        Integer[] arr = {1, 3, 4, 5, 6, 67};
        //使用toString方法输出数组
        System.out.println(Arrays.toString(arr));//[1, 3, 4, 5, 6, 67]
        //演示sort方法的使用
        Integer[] arr2 = {1, 3, 78, 132, 45, 12314};
        //数组是引用类型,所有通过排序后会影响到实参
        //也可以手搓排序法如冒泡选择等
        //sort是重载的,也可以传入接口
        System.out.println("排序前");
        System.out.println(Arrays.toString(arr2));
        Arrays.sort(arr2);
        System.out.println("排序后");
        System.out.println(Arrays.toString(arr2));
        //定制排序
        Arrays.sort(arr, new Comparator() {
            public int compare(Object o1, Object o2) {
                Integer i1 = (Integer) o1;
                Integer i2 = (Integer) o2;
                return i1 - i2;//>0就从小到大排小于0反之
            }
        });//接口编程+动态绑定+匿名内部类
        //演示二叉查找法,要求是有序的如果是无序的则无法使用
        //如果数组中不存在该元素就返回-(low+1);low是他应该存在的位置(如果存在)
        Integer[] arr3 = {1, 3, 7, 8, 24, 45, 124};
        int index = Arrays.binarySearch(arr3, 9);//-5
        int index2 = Arrays.binarySearch(arr3, 8);//3
        System.out.println(index);
        //copyOf数组元素赋值,从arr3数组中拷贝n个元素,
        // 如果长度不够,就在新数组后面添加空NUll,小于0抛出异常
        Integer[] newArr = Arrays.copyOf(arr3, arr3.length + 1);
        System.out.println(Arrays.toString(newArr));
        //fill数组填充
        Integer[] num = new Integer[]{1,4,8,5,9};
        Integer[] num1 = new Integer[]{1,4,5,8,9};
        //使用99填充数组num,替换原来的元素
        //        Arrays.fill(num,99);
        System.out.println(Arrays.toString(num));
        //equals比较两个数组元素内容是否完全一样包括顺序
        System.out.println(Arrays.equals(num,num1));
        //asList将一组值转换为list
    }
}

System类

编辑

BigInteger和BigDecimal类

用于处理很大的数据或很高的精度

编辑

Date类

package Date_;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author 刘宇
 */
public class Date01 {
    public static void main(String[] args) {
        Date d1 = new Date();//获取当前系统时间
        System.out.println(d1);//默认输出格式是国外的
        //可以使用SimpleDateFormat来规定格式,但格式字母是有规定的
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
        System.out.println(sdf.format(d1));
    }
}

Calendar类

介绍

1.Calendar类是一个抽象类,且构造器是受保护的

2.可以通过getInstance()来获取实例

3.提供有大量的方法和字段供使用

package Date_;

import java.util.Calendar;

/**
 * @author 刘宇
 */
public class Calendar_ {
    public static void main(String[] args){
        Calendar c =Calendar.getInstance();//创建日历类对象
        System.out.println("c="+c);
        //获取日历对象的某个日历字段
        System.out.println("年="+c.get(Calendar.YEAR));
        System.out.println("月="+(c.get(Calendar.MONTH)+1));//月从0开始编号
        System.out.println("日"+(c.get(Calendar.DAY_OF_MONTH)));
        System.out.println("小时"+(c.get(Calendar.HOUR_OF_DAY)));
        System.out.println("分钟"+(c.get(Calendar.MINUTE)));
        System.out.println("秒"+(c.get(Calendar.SECOND)));
        //Calendar没有专门的格式化方法,所以需要程序员自己来组合显示
        System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月" +(c.get(Calendar.DAY_OF_MONTH)) +"日"+(c.get(Calendar.HOUR_OF_DAY))+"小时"+(c.get(Calendar.MINUTE))+"分钟"+(c.get(Calendar.SECOND)) +"秒");
    }
}

第三代日期类

编辑

import java.time.LocalDateTime;  
import java.time.format.DateTimeFormatter;  
  
public class Main {  
    public static void main(String[] args) {  
        // 获取当前日期和时间  
        LocalDateTime now = LocalDateTime.now();  
  
        // 格式化日期和时间  
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");  
        String formattedDateTime = now.format(formatter);  
  
        // 输出格式化后的日期和时间  
        System.out.println("当前日期和时间(格式化后):" + formattedDateTime);  
    }  
}

集合package Collection_;

好处与介绍

1.可以动态保存任意多个对象,使用较为方便

2.提供一系列方便操作对象的方法:add,remove,set,get等

3.使用集合添加,删除新元素的示意代码-简介了

4.分为单列集合和双列集合

框架图

Collection

编辑

Map

编辑

Collection

Collection接口和常用方法

1.Collection实现子类可以存放多个元素,每个元素可以是Object

2.有些Collection的实现类是有序的(List),有些是无序的(Set)

3.Collection接口没有直接点实现子类,使用过他的子接口Set和List来实现的

List list = new ArrayList();
list.add("Hello");
list.add("World");
list.add(123);//list.add(new Integer(123))
System.out.println(list);
list.remove(0);//删除第一个元素
list.remove("Hello");//指定删除某个对象remove
System.out.println(list);
//查找某个元素是否存在,contains
System.out.println(list.contains("Hello"));
//返回元素个数,size
System.out.println(list.size());//2
//isEmpty:判断是否为空
System.out.println(list.isEmpty());
//clear清空
//        list.clear();
//        System.out.println(list);
//addAll添加多个元素
ArrayList list2 = new ArrayList();
list2.add("lans");
list2.add("java");
list.addAll(list2);
System.out.println(list);
//删除多个元素removeall
list.removeAll(list2);

迭代器遍历

Iterator iterator = c.iterator//得到一个c集合的迭代器

hasNext()用以判断是否还有下个元素

可利用循环迭代器遍历集合输出对象(快捷键itit)

next()作用:1.下移2.将下移以后集合位置上的元素返回

Collection c = new ArrayList();
        c.add(new Book("三国演义",100,"罗贯中"));
        c.add(new Book("小李飞刀",998,"古龙"));
        c.add(new Book("红楼梦",100,"曹雪芹"));
//以迭代器遍历 可用快捷键itit
        Iterator iterator = c.iterator();
        while(iterator.hasNext()){//判断是否还有数据
//            返回下一个元素,类型为Object
            Object object  = iterator.next();
            System.out.println(object);//调用重写后的toString方法
            //动态绑定,取决于存放的什么类型的元素就调用什么类型的方法
        }

集合增强for循环

快捷方式:输入一个I

//增强for底层仍是迭代器,book类似i是迭代变量
for(Object book:c){
    System.out.println(book);
}
//增强for也可以直接在数组中使用
for(int i:nums){
}

List接口

List集合类中元素有序,即(添加顺序和取出顺序一致)且可重复,都有索引,从0开始,实现接口很多,常用的包括ArrayList,LinkedList,Vector.

常用方法

编辑

遍历方式

List接口可以使用三种方式遍历:迭代器,增强for循环,普通for循环

List接口实现类--ArrayList

ArrayList可以存放数组,甚至多个数组,是由数组来实现数据储存的,ArrayList基本等同于Vector,除了ArrayList线程不安全(执行效率高),多线程不建议用ArrayList

ArrayList底层操作机制分析

结论:

1.ArrayList维护了一个Object类型的数组elementData

2.当创建ArrayList对象时,如果使用的无参构造器,则初始elementData容量为0,第一次添加则扩容,elementData为10,如需要再次扩容,则扩容elementData为1.5倍

3.如果使用指定大小的构造器,则初始elementData为指定大小,如需要再次扩容,则扩容elementData为1.5倍

List接口实现类--Vector

编辑

扩容机制分析

如果使用的无参构造器,则初始elementData容量为0,第一次添加则扩容,elementData为10,如需要再次扩容,则扩容elementData为2倍

如果指定大小,则每次按两倍扩容

List接口实现类--LinkedList

1.LinkedList底层维护了一个双向链表

2.LinkedList中维护了两个属性first last分别指向首节点和尾节点

3.每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表

4.所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

package Collection_;

/**
 * @author 刘宇
 */
public class LinkedList0 {
    public static void main(String[] args) {
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node cj = new Node("chenjing");
        Node smith = new Node("smith");
        //连接三个节点,形成双向链表
        //jack->tom->cj
        jack.next  = tom;
        tom.next = cj;
        //cj->tom->jack
        cj.pre = tom;
        tom.pre = jack;
        Node first = jack;//让first引用指向Jack,就是双向链表的头结点
        Node last = cj;//让last引用指向cj,就是双向链表的尾节点
        //演示遍历
        while(true){
            if(first == null){
                break;
            }
            System.out.println(first);
            first = first.next;
        }
        smith.next = cj;
        cj.pre = smith;
        smith.pre = tom;
        tom.next = smith;
        first = jack;
        last = cj;//让最后一个指对
        while(true){
            if(first == null){
                break;
            }
            System.out.println(first);
            first = first.next;
        }
    }
}

class Node{
    public Object item;//真正存放数据的
    //内部实例化
    public Node next;//指向后一个节点
    public Node pre;//指向前一个节点
    public Node(Object item){
        this.item = item;
    }
    public String toString(){
        return "item="+item;
    }
}

看add和remove的源码体会链表的巧妙

ArrayList和LinkedList的比较

编辑

Set接口

Set接口和常用方法

1.无序(添加和取出的顺序不一致),没有索引,但取出的顺序固定

2.不允许重复元素,所以最多包含一个null

3.JDK API中Set接口的实现类有:

和List接口一样,Set接口也是Collection的子接口.因此常用方法和Collection接口一样

Set接口的遍历方式

1.可以使用迭代器

2.增强for

3.接口对象,不能用索引遍历,所以无法用普通for循环

Set接口实现类--HashSet(**后)

1.HashSet实现了Set接口

2.HashSet实际上是HashMap

3.可以存放null,但只能有一个

4.HashSet不保证元素都是有序的,取决于hash后,再确定索引的结果

5.不鞥有重复元素或对象,但重名的不同对象可以(加了new出来的)

set.add(new Dog("tom"));

set.add(new Dog("tom"));可以

但是

set.add(new String("junan"));

set.add(new String("junan"));//不行,因为String类重写了equals方法,5

编辑

一个小栗子

package Collection_.set_;

import java.util.HashSet;
import java.util.Objects;

/**
 * @author 刘宇
 */
public class HashSetEx2 {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee1("小罗",6000,new MyDate(2005,4,11)));
        hashSet.add(new Employee1("小罗",6000,new MyDate(2005,4,11)));
        System.out.println(hashSet);
    }
}

class Employee1 {
    private String name;
    private int salary;
    private MyDate birthday;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee1 employee1 = (Employee1) o;
        return salary == employee1.salary && Objects.equals(name, employee1.name) && Objects.equals(birthday, employee1.birthday);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, salary, birthday);
    }

    @Override
    public String toString() {
        return "Employee1{" +
        "name='" + name + '\'' +
        ", salary=" + salary +
        ", birthday=" + birthday +
        '}';
    }

    public Employee1(String name, int salary, MyDate birthday) {
        this.name = name;
        this.salary = salary;
        this.birthday = birthday;
    }
}
class MyDate {
    private int year;
    private int month;
    private int day;

    @Override
    public String toString() {
        return "MyDate{" +
        "year=" + year +
        ", month=" + month +
        ", day=" + day +
        '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MyDate myDate = (MyDate) o;
        return year == myDate.year && month == myDate.month && day == myDate.day;
    }

    @Override
    public int hashCode() {
        return Objects.hash(year, month, day);
    }

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }
    //get,set方法节约篇幅已省略
}

HashSet子类--LinkedHashSet介绍

1.LinkedHashSet是HashSet的子类

2.LinkedHashSet底层是一个LinkedHashMap,地产维护了一个数组+双向链表

3.LinkedHashSet根据元素提供的hashCode值来决定元素的存储位置,同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的

4.LinkedHashSet不允许重复添加元素

Map接口[很实用]

1.Map和Collection并列存在,用于保存具有映射关系的数据:Key-Value(双列元素)

2.Map中的Key和Value可以使任何引用类型的数据,会封装到HashMap$Node对象中

3.Map中的Key不能重复,原因和HashSet一样,新的Key会替换老的相同的Key

4.Map中的Value可以重复.(理解:Key门牌号,Value房主人)

5.Map中的Key可以为null,Value也可以为空,注意Key为null只能有一个,Value为空可以有多个

6.常用String类作为Key(用Object和其子类即可)

7.Key和Value之间存在单向一对一关系,即通过制定的Key可以找到制定的Value

Map map = new HashMap();
        map.put("no1","xl");//K-V
        map.put("no2","luo");
        map.put("no3","xl");
        map.put(null,null);
        map.put(null,"luo");//会替换null-null(K-V)
        map.put("no4",null);
        map.put(1,null);
        map.put(new Object(),"金毛狮王");
        System.out.println(map);//无序
        System.out.println(map.get("no1"));

Map接口常用方法

编辑

Map遍历六大方式

package Collection_.map_;

import java.util.*;

/**
 * @author 刘宇
 */
public class mapfor {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("ly","ljx");
        map.put("sm","nana");
        map.put("dai","nana");
        map.put("ji",null);
        map.put(null,"cj");
        map.put("a","b");
        //第一组:取出所有的Key,通过Key取出对应的Value
        Set keyset = map.keySet();
        //(1)增强for
        for(Object key:keyset){
            System.out.print(key+"-"+map.get(key));
        }
        //(2)迭代器
        Iterator iterator  = keyset.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.print(key+"-"+map.get(key));
        }
        //第二组:把所有的Value取出
        Collection values = map.values();
        //这里可以使用所有Collection的遍历方法
        //(1)增强for
        for(Object value:values){
            System.out.print(value);
        }
        //(2)迭代器
        Iterator iterator2 = values.iterator();
        while (iterator2.hasNext()) {
            Object next =  iterator2.next();
            System.out.print(next);
        }
        //第三组:通过EntrySet来获取k-v
        Set entrySet = map.entrySet();
        //(1)增强for
        for(Object entry:entrySet){
            //将Entry转为Map.Entry
            Map.Entry m = (Map.Entry)entry;
            System.out.print(m.getKey()+"-"+m.getValue());
        }
        //(2)迭代器
        Iterator iterator1 = entrySet.iterator();
        while (iterator1.hasNext()) {
            Object next =  iterator1.next();
            //            System.out.print(next.getClass());
            Map.Entry m = (Map.Entry)next;
            System.out.print(m.getKey()+"-"+m.getValue());
        }
    }
}

Map接口实现类-HashMap

小结:

1.Map接口常用实现类:HashMap,Hashtable,Properties

2.HashMap是以Map接口使用频率最高的实现类

3.HashMap是以key-value对的方式来存储数据的

4.key不能重复但值可以重复,允许使用null

5.如果添加相同的key就会覆盖原来的key(替换Value)

6.与HashSet也一样,不保证映射的顺序,因为底层是以hash表的方式来存储的

7.HashMap没有实现同步,因此是线程不安全的,方法没作同步互斥的操作,即synchronized

编辑

扩容机制和HashSet完全相同

编辑

Map接口实现类--Hashtable

编辑

扩容机制

编辑

与HashMap的对比

编辑

Map接口实现类--Properties

1.Properties类继承自Hashtable类并且实现了Map接口,也使用了一种键值对的形式来保存数据

2.它的使用特点和table类似

3.Properties还可以用于从xxx.properties文件,加载数据到Properties类对象,并进行读取和修改

4.xxx.properties文件通常作为配置文件.

常用方法

编辑

TreeSet

通过在创建对象的时候传入一个匿名内部类实现排序操作

package Collection_.set_;

import java.util.Comparator;
import java.util.TreeSet;
public class TreeSet_ {
    public static void main(String[] args) {
        //如果调用默认的构造器就不会排序
        //        TreeSet ts = new TreeSet();
        TreeSet ts = new TreeSet(new Comparator(){
            public int compare(Object o1, Object o2) {
                //下面调用String类的compareTo方法进行字符串的大小
                //从ASC码大小排序
                return ((String)o1).compareTo((String)o2);
                //return((String)02).length()-((String)01).length()按长度比较,相同长度的无法添加
            }
        });
        ts.add("luo");
        ts.add("a");
        ts.add("yu");
        ts.add("asafa");
        System.out.println(ts);
    }
}

Collections工具类

package Collection_;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @author 刘宇
 */
@SuppressWarnings({"all"})
public class Collectiona {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("luo");
        list.add("cj");
        list.add("liuyu");
        list.add("jijijiji");
        //reverse(List)反转List中的元素
        //        Collections.reverse(list);
        //shuffle(List)对List集合元素进行随机排序
        //        Collections.shuffle(list);
        //sort(List)进行排序,按字典序
        //        Collections.sort(list);
        //若想指定排序方式,可以传入一个匿名内部类
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return ((String) o1).length() - ((String) o2).length();
            }
        });
        //swap(List,int,int)将指定集合的list元素进行交换
        //        Object max(Collection):根据自然顺序,返回给定集合最大元素
        //Object max(Collection,new Comparator(){})
        //也可以返回指定指定顺序的最大元素,min方法参考max即可
        //int frequency(Collection,Object):返回指定集合中出现的制定元素次数
        //void copy(List dest,List src):将src中的内容复制到dest中去
        ArrayList dest = new ArrayList();
        for (int i = 0; i <list.size();i++){
            dest.add("");
        }
        //拷贝
        Collections.copy(dest,list);
        //        System.out.println(list);
        //        System.out.println("===========");
        //        System.out.println(dest);
        //boolean replaceAll(List list,Object oldVal,Object newVal);
        //使用性质替换List对象的所有旧值
        Collections.replaceAll(list,"luo","罗");
        System.out.println(list);
        System.out.println("===========");
        System.out.println(dest);
    }
}

总结:开发中如何选择集合实现类

1.判断存储的类型(一组对象[单列]或一组键值对[双列])

2.一组对象[单列]:Collection接口

1)允许重复:List

增删多:LinkedList[底层维护了一个双向链表]

改查多:ArrayList[底层维护Object的可变数组]

2)不允许重复:Set

无序:HashSet[底层是HashMap,维护了一个Hash表[即数组+链表+红黑树]

排序:TreeSet

插入和取出顺序一致:LinkedHashSet,维护数组+双向链表

3)一组键值对:

键无序:HashMap[底层是:哈希表 JDK8: 数组+链表+红黑树]

键排序:TreeMap

插入和取出顺序一致: LinkedHashMap

读取文件 Properties

泛型

泛型的好处

1.编译时能检查元素类型,提高安全性

2.减少了类型转换的次数,提高效率

3.不再提示编译警告

泛型介绍

1.又称参数化类型,是一种表示数据类型的数据类型,是JDK5.0出现的新特性,解决数据类型的安全性问题.

2.在类声明或实例化时只要指定好需要的具体化类型即可

3.Java泛型可以保证如果程序咋编译时没有发出警告,在运行时就 不会产生ClassCastException异常,同时,代码更简洁,健壮

4.泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者某个方法的返回值类型,或是参数类型,在编译期间就指定好

泛型语法

编辑

注意事项,细节

1.给泛型指定的数据必须是引用类型,不能是基本数据类型,T,K,V是引用类型

2.指定泛型的具体类型后,可以传入该类型或该子类类型

3.可以简写,List<Integer>list1 = new ArrayList<Integer>();

List<Integer>list2 = new ArrayList<>();,编译器会进行类型推断

4.List list2 = new ArrayList()等价于List<Object>list2 = new ArrayList<>();

当你看到<A>这样的泛型声明时,它实际上是在说“这个类、接口或方法将使用某个类型,但我们现在不具体指定这个类型是什么;它将在使用这个类、接口或方法时被指定”。

泛型应用举例

package Generic_;


import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * @author 刘宇
 */
public class genericEx02 {
    public static void main(String[] args) {
        List<Employee> arraylist = new ArrayList<>();
        arraylist.add(new Employee("张有为",4000,new MyDate1(5,2,11)));
        arraylist.add(new Employee("罗",14000,new MyDate1(5,8,11)));
        arraylist.add(new Employee("罗婧萱",14000,new MyDate1(5,2,12)));
        arraylist.add(new Employee("宇",4000,new MyDate1(5,8,10)));
        arraylist.sort(new Comparator<Employee>(){
            @Override
            public int compare(Employee o1,Employee o2){
                int a = o1.getName().compareTo(o2.getName());//按字典顺序比较!!老忘记还以为写错了
                if(a!=0)
                    return a;
                else
                    return bijiao(o1.getBirthday(),o2.getBirthday());
            }
        });
        System.out.println(arraylist);
    }
    public static int bijiao(MyDate1 d1, MyDate1 d2){
        if(d1.getYear()>d2.getYear()){
            return 1;
        }else if(d1.getYear()<d2.getYear()){
            return -1;
        }else if(d1.getMonth()>d2.getMonth()){
            return 1;
        }else if(d1.getMonth()<d2.getMonth()){
            return -1;
        }else if(d1.getDay()>d2.getDay()){
            return 1;
        }else if(d1.getDay()<d2.getDay()){
            return -1;
        }
        return 0;
    }
}

class Employee {
    private String name;
    private int salary;
    private MyDate1 birthday;

    @Override
    public String toString() {
        return "Employee{" +
        "name='" + name + '\'' +
        ", salary=" + salary +
        ", birthday=" + birthday +
        '}';
    }

    public Employee(String name, int salary, MyDate1 birthday) {
        this.name = name;
        this.salary = salary;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public MyDate1 getBirthday() {
        return birthday;
    }

    public void setBirthday(MyDate1 birthday) {
        this.birthday = birthday;
    }
}

class MyDate1 {
    private int year;
    private int month;
    private int day;

    public MyDate1(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return "MyDate1{" +
        year +
        ":" + month +
        ":" + day +
        '}';
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }
}

自定义泛型

基本语法

class 类名<T,R,...>{...}

注意细节

1.普通成员可以使用泛型

2.使用泛型的数组,不能初始化

3.静态方法中不能使用类的泛型

4.泛型类的类型,是在创建对象时确定的(因为创建对象时需要指定确定类型)

5.如果在创建对象时,没有指定类型,则默认为Object

自定义泛型接口

基本语法

interface 接口名<T,R...>{}

注意细节

1.接口中,静态成员也不能使用泛型

2.泛型接口的类型,在继承接口实现接口的时候确定

3.不指定就默认Object

自定义泛型方法

基本语法

修饰符<T,R...>返回类型 方法名(T t,R r...){...}

注意细节

1.泛型方法可以定义在普通类中,也可以定义在泛型类中

2.当泛型方法被调用时,类型就会确定

3.public void eat(E e){},修饰符后没有<T,R...>,eat不是泛型方法只是使用了泛型

泛型继承和通配符

1.泛型不具备继承性

2.<?>支持任意泛型

<? extends A>:支持A类及其A类子类,规定了泛型的上限

<? super A>:支持A类及其父类,不限于直接父类,规定了泛型的下线

//说明:List<?> 表示任意的泛型类型都可以接受
public static void printCollection1(List<?>c){
    for(Object o : c){
        System.out.println(o);
    }
}
//List<?extends AA>表示上限,可以接受AA或者AA子类
public static void printCollection2(List<? extends AA> c){
    for(Object o:c){
        System.out.println(o);
    }
}
//List<? super AA>支持AA类及其父类,不限于直接父类
public static void printCollection3(List<? super AA>c){
    for(Object o:c){
        System.out.println(o);
    }
}

JUnit使用

1.一个类有很多功能代码需要测试,为了测试,就需要写入main方法中

2.如果有多个功能代码测试,需要来回注销很麻烦

3.可以直接运行一个方法,就方便许多,并且可以给出相关信息->JUnit

搜索配置视频,添加Junit5到IDEA即可

加深理解例子

package Generic_;

import org.junit.jupiter.api.Test;

import java.util.*;

/**
 * @author 刘宇
 */
@SuppressWarnings({"all"})
public class homework10086 {
    public static void main(String[] args) {

    }
    @Test
    public void testList(){
        DAO<User> dao = new DAO<>();
        dao.save("001",new User(1,2,"jack"));
        dao.save("002",new User(2,22,"luo"));
        List<User> list = dao.list();
        System.out.println(list);
    }
}

class DAO<T>{
    Map<String,T> map = new HashMap();
    public void save(String id,T entity){
        map.put(id,entity);
    }
    public T get(String id){
        return (T)map.get(id);//向下转型
    }
    public void update(String id,T entity){
        map.put(id,entity);
    }
    public List<T>list(){
        List<T> list = new ArrayList<>();//使用ArrayList来存储Map的值
        Set<String> c = map.keySet();//获取map的值
        Iterator iterator = c.iterator();//迭代器来遍历
        while (iterator.hasNext()) {
            Object next = iterator.next();
            //map.get(next)返回的就是User对象->ArrayList
            list.add((T)map.get(next));
        }
        return list;
    }
    public void delete(String id){
        map.remove(id);
    }
}
class User{
    private int id;
    private int age;
    private String name;

    public int getId() {
        return id;
    }

    @Override
    public String toString() {
        return "User{" +
        "id=" + id +
        ", age=" + age +
        ", name='" + name + '\'' +
        '}';
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }
}

Java事件处理机制

当事件发生时,产生事件的对象,会把此信息传递给事件的监听者处理,这里说的信息实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为事件的对象

事件源:产生事件的对象,比如按钮,窗口等

事件:事件就是承载事件源状态改变时的对象..比如遇到键盘事件,鼠标事件,窗口事件等等,会生成一个事件对象,该对象保存着当前事件的很多信息,

java.awt.event包和javax.swing.event包中定义了各种事件类型

事件监听接口:

一个小栗子,小球移动

package com.lystudy.event;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * @author 刘宇
 * 演示小球通过键盘控制上下左右移动
 */
public class BallMove extends JFrame {
    MyPanel mp;

    public static void main(String[] args) {
        BallMove bm = new BallMove();
    }

    //构造器
    public BallMove() {
        mp = new MyPanel();
        this.add(mp);
        this.setSize(500, 500);//设置画板大小
        //窗口JFrame对象可以监听键盘事件
        this.addKeyListener(mp);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }
}

//面板画小球
//KeyListener是一个监听器,可以监听键盘事件
class MyPanel extends JPanel implements KeyListener {
    //为了让小球可以移动,把它左上角的坐标(x,y)设置变量
    int x = 10;
    int y = 10;

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.red);
        g.fillOval(x, y, 20, 20);
    }

    //有字符输出时,该方法就会触发
    @Override
    public void keyTyped(KeyEvent e) {

    }

    //当某个键按下,该方法会触发
    @Override
    public void keyPressed(KeyEvent e) {
        //        System.out.println("按下的是"+(char)e.getKeyCode());
        //根据用户按下的不同键,来处理小球的移动
        //Java会给每一个键分配一个值
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {//KeyEvent_VKDOWN就是对应的向下的箭头
            y += 5;
        }else if (e.getKeyCode() == KeyEvent.VK_UP) {
            y -= 5;
        }else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            x -= 5;
        }else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            x += 5;
        }
        //让面板重绘
        this.repaint();
    }

    //当某个键松开,触发该方法
    @Override
    public void keyReleased(KeyEvent e) {

    }
}

线程基础

进程

1.进程指运行中的程序

2.进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:

有他自身的产生存在消亡的过程.

线程

1.线程由进程创建,是进程的一个实体

2.一个进程可以拥有多个线程

单线程

同一个时刻只允许执行一个线程

多线程

同一个时刻,可以执行多个线程

并发

同一个时刻,多个任务交替执行,造成一种貌似同时的错觉,简单的说,单核cpu实现的多任务就是并发

并行:

同一个时刻,多个任务同时执行.多核CPU可以并行.并发并行可以同时存在

启动线程,start会调用run方法
如果直接调用run方法,run方法只是普通的方法,没有真正的启动一个线程,把run方法执行完毕后才向下执行会阻塞线程
//start里面会调用start0方法,start0是本地方法,由JVM调用,底层c/C++实现

Runnable接口

runnable 接口在 Java 中是一个用于指定线程执行体的功能接口。它属于 java.lang 包,是 Java 并发编程的基础之一。当一个类实现了 Runnable 接口,它必须实现 run() 方法,这个方法定义了线程执行的操作。Runnable 接口的引入使得 Java 的线程创建更加灵活,因为它允许你将线程的执行代码与线程对象本身分离,即实现 Runnable 接口的类的实例可以被用来创建多个线程,每个线程都执行相同的 run() 方法。通过代理模式调用start0

package SellTicket;

import jdk.nashorn.internal.ir.CallNode;

/**
 * @author 刘宇
 */
public class SellTicket01 {
    public static void main(String[] args) {
        //        T t1 = new T();
        //        t1.start();
        //        T t2 = new T();
        //        t2.start();
        //         T t3 = new T();
        //        t3.start();
        T3 t3 = new T3();
        Thread th1 = new Thread(t3);
        Thread th2 = new Thread(t3);
        Thread th3 = new Thread(t3);
        th1.start();
        th2.start();
        th3.start();
    }
}
/*
class T extends Thread{
    public static int ticknums =100;
    @Override
    public void run() {
        while(true){
            if(ticknums<=0){
                System.out.println("票已卖完");
                break;
            }
            System.out.println("线程"+Thread.currentThread().getName()+"售出一张票"+(--ticknums));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}*/
class T3 implements Runnable{
    public static int ticknums =100;
    @Override
    public void run() {
        while(true){
            if(ticknums<=0){
                System.out.println("票已卖完");
                break;
            }
            System.out.println("线程"+Thread.currentThread().getName()+"售出一张票"+(--ticknums));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

通知线程退出

将循环条件加入到主方法(即主线程中),即使用变量来控制run方法退出,即通知方式.在一个线程修改循环条件

线程常用方法

线程的礼让:yield静态方法 ,线程的礼让.让出CPU,让其他线程执行,但礼让的时间不确定,所以不一定礼让成功

线程插队:join()线程的插队.一旦插队成功,则肯定先执行完插入的线程所有任务

package com.lystudy.Treaduse;

/**
 * @author 刘宇
 */
public class ThreadMethod01 {
    public static void main(String[] args) {
        T t1 = new T();
        Thread t2 = new Thread(t1);
        for (int i = 1; i <= 10; i++) {
            System.out.println("hi" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i == 5) {
                t2.start();
                try {
                    t2.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class T implements Runnable {
    public static int num = 10;
    @Override
    public void run() {
        for (int i = 1; i <= num; i++) {
            System.out.println("hello" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

用户线程: 也叫工作线程,当线程的任务执行完了或者通知方式结束

守护线程:一般为工作线程服务的,当所有的用户线程结束,守护线程自动结束.方式: 对象名.setDaemon(true)

常见的守护线程:垃圾回收机制

线程的七种状态

new 新建状态 Runnable可运行状态(包括 Ready 就绪状态 Running运行中两种状态) Blocked 阻塞状态

Waiting 等待状态 Timed_Waiting定时等待状态 Terminated 终止状态

线程同步机制

1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性

2.理解:线程同步,即当一个线程在对内存进行操作时,其他线程不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作.

具体代码块

1.synchronized(对象){//得到对象的锁,才能操作同步代码

//需要被同步的代码

}

2.synchronized还可以放在方法声明中,表示整个方法-未同步方法

public synchronized void m1(String name){

//需要被同步的代码

}

利用synchronized解决卖票问题,将卖票方法上锁,使三个线程不能同时进入卖票方法,这样就解决了超票的问题

互斥锁synchronized

1.每个对象都对应一个可称为"互斥锁的标记",这个标记用来保证在任一时刻,只能有一个线程访问该对象.

2.关键字synchronized来与对象的互斥锁联系.当某个对象用synchronized修饰时,表明该对象在任一时刻只能有一个线程访问.

3.同步的局限性:导致程序执行效率要降低

4.对于同步方法(非静态),它们默认的锁可以是对象本身this,这意味着,如果一个类的实例上有多个非静态同步方法,那么在任意时刻,只有一个线程可以执行该实例上的任意一个同步方法。这是因为这些同步方法都在竞争同一个锁(即实例对象本身)。也可以是其他对象(要求是同一个对象).然而,虽然默认情况下非静态同步方法使用this作为锁,但你也可以通过让同步方法或同步代码块(synchronized block)显式地指定一个不同的对象作为锁,来允许不同实例之间的同步方法或代码块进行互斥。但是,为了达到这个目的,你必须在所有的同步方法或代码块中使用同一个对象作为锁。

public class MyClass {  
    private final Object lock = new Object();  
  
    public synchronized void syncMethod1() {  
        // 默认使用this作为锁  
    }  
  
    public void syncMethod2() {  
        synchronized(lock) {  
            // 使用lock对象作为锁  
        }  
    }  
}
/*在这个例子中,syncMethod1使用this作为锁,而syncMethod2使用了一个名为lock的单独对象
作为锁。这意味着,尽管这两个方法都是同步的,但它们并不互相阻塞,因为它们的锁对象不同。
但是,如果syncMethod2内部再有一个同步代码块使用this作为锁,那么它将与syncMethod1
竞争同一个锁。*/

5.同步方法(静态的)的锁为当前类本身,对于静态的同步方法,它们的锁是类的Class对象,而不是类的某个实例。这意味着,如果有一个类有多个静态同步方法,那么在任意时刻,只有一个线程可以执行这个类上的任意一个静态同步方法。这是因为这些静态同步方法都在竞争同一个锁(即类的Class对象)。

互斥锁落地的实现步骤

1.先分析需要上锁的代码

2.选择同步代码块或同步方法

3.要求多个线程的锁对象时同一个!

死锁

死锁(Deadlock)是指两个多个进程(或线程)在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,他们都将无法向前推进.

示例代码

java复制代码



public class DeadlockExample {  
    private static Object resource1 = new Object();  
    private static Object resource2 = new Object();  
  
    public static void main(String[] args) {  
        // 线程1  
        Thread thread1 = new Thread(() -> {  
            synchronized (resource1) {  
                System.out.println("Thread 1: Holding resource 1...");  
                try {  
                    Thread.sleep(1000); // 模拟耗时操作  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized (resource2) {  
                    System.out.println("Thread 1: Holding resource 1 and resource 2...");  
                }  
            }  
        });  
  
        // 线程2  
        Thread thread2 = new Thread(() -> {  
            synchronized (resource2) {  
                System.out.println("Thread 2: Holding resource 2...");  
                try {  
                    Thread.sleep(1000); // 模拟耗时操作  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized (resource1) {  
                    System.out.println("Thread 2: Holding resource 2 and resource 1...");  
                }  
            }  
        });  
  
        thread1.start();  
        thread2.start();  
    }  
}

死锁解释

  1. 锁资源:在这个例子中,我们定义了两个资源resource1resource2,它们都是Object类型的静态变量。
  2. 线程行为
    • 线程1首先尝试获取resource1的锁,并在获取后执行一些操作(模拟耗时操作)。随后,它尝试获取resource2的锁。
    • 线程2的行为与线程1类似,但它首先尝试获取resource2的锁,并在获取后执行一些操作。随后,它尝试获取resource1的锁。
  1. 死锁条件
    • 互斥条件:每个资源只能被一个线程持有。
    • 持有并等待条件:线程1持有resource1并等待resource2,而线程2持有resource2并等待resource1
    • 非抢占条件:资源不能被强制从线程中剥夺。
    • 循环等待条件:存在一个线程等待链,其中每个线程都在等待链中下一个线程持有的资源。
  1. 结果
    • 由于线程1和线程2都在等待对方释放资源,而它们各自持有的资源又是对方所需的,因此这两个线程都将无限期地等待下去,形成死锁。

解决方案

要避免死锁,可以采取以下策略:

  • 确保一致的锁顺序:所有线程都按照相同的顺序请求锁。
  • 使用超时锁:尝试获取锁时设置超时时间,如果超时则释放已获得的锁,并稍后重试。
  • 避免嵌套锁:尽量减少在持有多个锁的同时请求其他锁的情况。
  • 使用锁管理工具:利用Java并发工具包(如java.util.concurrent)中的高级锁管理工具,如ReentrantLockLockSupport,它们提供了更灵活的锁控制和死锁检测功能。

通过这些策略,可以有效地减少或避免Java程序中的死锁问题。

释放锁

下面操作会释放锁

1.当前线程的同步方法,同步代码块执行结束

2.当前线程在同步代码块,同步方法中遇到return,break

3.当前线程在同步代码块同步方法中出现了未处理的Error或Exception,导致异常结束

4.当前线程在同步代码块,同步方法中执行了线程对象的wait方法,当前线程暂停,并释放锁

下面操作不会释放锁

1.执行同步方法或同步代码块时,程序调用Thread.sleep();或Thread.yield()方法暂停当前线程的执行,不会释放锁

2..执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,不会释放锁

package com.lystudy.Treaduse;

/**
 * @author 刘宇
 */
public class homework02 {
    public static void main(String[] args) {
        GetMoney getMoney = new GetMoney();
        Thread t1 = new Thread(getMoney);
        t1.setName("t1");
        Thread t2 = new Thread(getMoney);
        t2.setName("t2");
        t1.start();
        t2.start();
    }
}

class GetMoney implements Runnable {
    private int money = 10000;
    @Override
    public void run() {
        while (true) {
            synchronized (this) {
                if (money < 1000) {
                    System.out.println("无钱可取了");
                    break;
                }
                money -= 1000;
                System.out.println(Thread.currentThread().getName() + "取钱成功,还剩" + money);
            }
            //休眠放锁外面,放里面不会释放锁,这样整个while循环都在锁中,就无法实现多线程进入
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

IO流

文件

保存数据的地方.

文件流

文件在程序中是以流的形式来操作的

常见的文件操作

创建文件对象相关的构造器和方法

new File(String pathname)//根据路径构建一个File对象

new File(File parent,String child)//根据父目录文件+子路径构建

new File(String parent,String child)//根据父目录+子路径构建

createNewFile创建新文件

File file = new File("D:\\news1.txt");
System.out.println("文件名字="+file.getName());
System.out.println("文件绝对路径"+file.getAbsolutePath());
System.out.println("文件父级目录"+file.getParent());
System.out.println("文件大小(字节)"+file.length());
System.out.println("文件是否存在"+file.exists());//T
System.out.println("是不是一个文件"+file.isFile());//T
System.out.println("是不是一个目录"+file.isDirectory());//F

目录操作

目录也被当做文件

IO流原理和分类

1.I/O即Input/Output,用于处理数据传输.

2.java程序中,对于数据的输入/输出操作以"流"的方式进行

3.java.io包下面提供了各种"流"和接口,用以获取不同种类的数据,并通过方法输入或输出数据

4.输入input:读取外部数据(磁盘,光盘等存储设备的数据)到程序中(内存)中

5.输出output:将程序(内存)数据输出到磁盘,光盘等存储设备中.

文件字节流

FileInputStream

FileInputStream 常用方法

  1. FileInputStream(String name)
    • 构造一个 FileInputStream 来从指定名称的文件(name)读取数据。
  1. FileInputStream(File file)
    • 构造一个 FileInputStream 来从 File 对象指定的文件中读取数据。
  1. int read()
    • 从此输入流中读取下一个数据字节。如果到达文件末尾,则返回 -1
  1. int read(byte[] b)
    • 从此输入流中将最多 b.length 个字节的数据读入字节数组 b 中。
  1. int read(byte[] b, int off, int len)
    • 从此输入流中将最多 len 个字节的数据读入字节数组 b 中,从偏移量 off 处开始存储数据。
  1. void close()
    • 关闭此文件输入流并释放与此流相关联的所有系统资源。

FileOutputStream

FIleOutputStream常用方法

  1. FileOutputStream(String name)
    • 构造一个 FileOutputStream 来向指定名称的文件(name)写入数据。如果文件已存在,则会被覆盖。
  1. FileOutputStream(File file)
    • 构造一个 FileOutputStream 来向 File 对象指定的文件写入数据。如果文件已存在,则会被覆盖。
  1. FileOutputStream(String name, boolean append)
    • 构造一个 FileOutputStream 来向指定名称的文件(name)写入数据。如果第二个参数为 true,则数据将被写入文件末尾,而不是覆盖原有内容。
  1. FileOutputStream(File file, boolean append)
    • 构造一个 FileOutputStream 来向 File 对象指定的文件写入数据。如果第二个参数为 true,则数据将被写入文件末尾,而不是覆盖原有内容。
  1. void write(int b)
    • 将指定的字节写入此文件输出流。
  1. void write(byte[] b)
    • b.length 个字节从指定字节数组写入此文件输出流中。
  1. void write(byte[] b, int off, int len)
    • 将指定字节数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
  1. void close()
    • 关闭此文件输出流并释放与此流相关联的所有系统资源
package com.lystudy.IO.OutPutStream_;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * @author 刘宇
 */
public class FileCopy {
    public static void main(String[] args) {
        //完成文件拷贝,将 D:\\junjun.jpg 拷贝到c:\\
        //思路分析
        //1.创建文件输入流 将文件读入到程序
        //2.创建文件的输出流 将读取到的问句数据写到指定位置
        String srcfilePath = "D:\\junjun.jpg ";
        String destfilePath = "D:\\植物\\junjun.jpg ";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream(srcfilePath);
            fileOutputStream = new FileOutputStream(destfilePath);
            byte[] buffer = new byte[1024];
            int readlen = 0;
            while((readlen = fileInputStream.read(buffer))!=-1){
                //读取到后,就写文件,通过fileOutputStream
                //即:边读边写
                fileOutputStream.write(buffer,0,readlen);//防止文件损失,一定要用这个方法
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                //关闭流释资源
                if(fileInputStream!=null){
                    fileInputStream.close();
                }
                if(fileOutputStream!=null){
                    fileOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

文件字符流

FileRead

FileWriter

节点流和处理流

1.节点流可以从一个特定的数据源读写数据,如FileReader,FileWriter

2.处理流(也叫包装流)是"连接"在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,如BufferedReader,BufferedWriter.

节点流和处理流的区别和联系

1.节点流是底层流/低级流,直接跟数据源相接

2.处理流(包装流)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出.

3.处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连.

处理流

处理流的功能主要体现在以下两个方面:

1.性能的提高: 主要以增加缓冲的方式来提高输入输出的效率.

2.操作的便捷: 处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便.设计模式中的处理流设计模式

BufferedReader和BufferedWriter

同样true不true决定是覆盖还是追加

BufferedReader 常用方法

  1. BufferedReader(Reader in):构造函数,创建一个新的 BufferedReader,给定一个 Reader 对象。
  2. void close():关闭流并释放与之关联的所有资源。
  3. String readLine():读取包含下一行内容的字符串。如果已到达文件末尾,则返回 null
  4. int read(char[] cbuf, int off, int len):将字符读入数组的某一部分。
  5. boolean ready():检查此流是否已准备好被读取。

BufferedWriter 常用方法

  1. BufferedWriter(Writer out):构造函数,创建一个新的 BufferedWriter,给定一个 Writer 对象。
  2. void close():关闭流并释放与之关联的所有资源。
  3. void write(char[] cbuf, int off, int len):写入字符数组的某一部分。
  4. void write(int c):写入单个字符。
  5. void write(String s, int off, int len):写入字符串的某一部分。
  6. void newLine():写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,它根据底层操作系统而有所不同。
  7. void flush():刷新该流的缓冲,即强制写出所有缓冲的输出字符到目的地。如果目的地是另一个字符流,则刷新那个流。

实例

package com.lystudy.IO.File_;

import java.io.*;

/**
 * @author 刘宇
 */
public class BufferedCopy_ {
    public static void main(String[] args){
        //BufferedReader和BufferedWriter进行的是字符操作,
        // 最好不要用它来操作二进制文件,否则可能造成文件损坏
        String srcFilePath = "D:\\hello.txt";
        String destFilePath = "D:\\a.txt";
        String line;
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            br = new BufferedReader(new FileReader(srcFilePath));
            bw = new BufferedWriter(new FileWriter(destFilePath));
            //readLine读取一行内容,但没有换行
            while((line = br.readLine()) != null){
                bw.write(line);
                //插入一个换行
                bw.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                if(br != null){
                    br.close();
                }
                if(bw != null){
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Buffered字节处理流

对象处理流ObjectOutputStream ObjectInputStream

序列化和反序列化

1.序列化就是在保存数据时,保存数据的值和数据类型

2.反序列化就是在恢复数据时,恢复数据的值和数据类型

3.需要让某个对象支持序列化机制,则必须让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable 这是一个标记接口,没有方法/Externalizable(该接口有方法需要实现,因此一般实现Serializable)

package com.lystudy.IO.OutPutStream_;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * @author 刘宇
 * 演示ObjectOutputStream和ObjectInputStream
 */
public class ObjectOutputStream_ {
    public static void main(String[] args){
        String filePath = "D:\\data.dat";
        try {
            //序列化后保存的文件格式不是纯文本,而是按照他的格式来保存
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
            //序列化数据到D:\data.dat
            oos.writeInt(100);//int->Integer(实现了Serializable)
            oos.writeBoolean(true);//boolean->Boolean(实现了Serializable)
            oos.writeChar('a');//char->Char(实现了Serializable)
            oos.writeDouble(3.14);//double->Double(实现了Serializable)
            oos.writeFloat(3.14f);//float->Float(实现了Serializable)
            oos.writeUTF("liuyu");//String(实现了Serializable)
            //保存;一个Dog对象
            oos.writeObject(new Dog("小七",5));

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class Dog implements Serializable {
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

注意事项:

1.读写顺序要一致

2.要求实现序列化或反序列化的对象,需要实现Serializable

3.序列化的类中建议添加SerialVersionUID,为了提高版本兼容性

4.序列化对象时,默认将里面的所有属性都进行序列化,但除了static和transient修饰的成员

5.序列化对象时,要求里面属性的类型也要实现序列化接口

6.序列化具备可继承性,也就是如果某类已经实现了序列化,则他的所有子类也已经默认实现了序列化

标准输入输出流

转换流-InputStreamReader和OutputStreamWriter

1.InputStreamReader:Read的子类,可以将InputStream(字节流)包装成(转换成)Reader字符流

2.OutputStreamWriter:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流)

3.当处理纯文本数据时,如果使用字符流效率更高,冰洁可以有效的解决中文问题,所以建议将字节流转换成字符流

4.可以在使用时指定编码格式(比如 utf-8,gbk,gb2312,ISO8859-1等)

打印流-PrintStream和PrintWriter 只有输出

  • PrintStream:是字节打印流类,继承自FilterOutputStream,主要操作byte流。它适合用于二进制文件(非文本)的处理,也可以用于文本文件,但在处理中文等字符时可能会遇到字符集乱码问题。
  • PrintWriter:是字符打印流类,继承自Writer类。它主要操作字符流,适合用于文本文件的处理,跨平台性较好。在处理中文和特殊字符时,PrintWriter更加灵活,因为它可以指定字符编码。

Properties

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值