Java SE重点知识笔记

前言:
         学了半年的Java web 发现还有一些JavaSE的知识没有掌握,趁着这个暑假把java的基础知识补一下。


1、String对象的特点

1、创建字符串对象两种方式的区别

  • 通过构造方法创建

通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同。

上面的代码中,JVM会首先创建一个字符数组, 然后每一次new的时候都会有一 个新的地址, 只不过s1和s2参考的字符串内容是相同的。

  • 直接赋值方式创建

""方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护。

在上面的代码中,针对第一行代码, JVM会建立一个 String对象放在字符串池中,并给s3参考,第二行则让s4直接参考字符串池中的String对象,也就是说它们本质上是同一个对象。

2、字符串的比较

1、==号的作用 :

  • 比较基本数据类型:比较的是具体的值
  • 比较引用数据类型:比较的是对象地址值

2、equals方法的作用:

因为String是类,属于引用类型,所以用equals来进行比较。

public boolean equals(String s)   比较两个字符串内容是否相同、区分大小写


2、StringBuilder对象

1、StringBuilder概述

  • StringBuilder是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是StringBuilder对象中的内容是可变的。

String和StringBuilder的区别:

  • String: 内容是不可变的
  • StringBuilder: 内容是可变的

2、StringBuilder类的构造方法

常用的构造方法:

示例代码:

public class StringTest2 {

    public static void main(String[] args) {
        // public StringBuilder() :创建一个空白可变字符串对象,不含有任何内容
        StringBuilder sb = new StringBuilder();

        System.out.println("sb:" + sb);
        System.out.println("sb. length():" + sb.length());

        // public Stri ngBuilder(String str) :根据字符串的内容,来创建可变字符串对象
        StringBuilder sb2 = new StringBuilder("he11o");
        System.out.println("sb2:" + sb2);
        System.out.println("sb2. length():" + sb2.length());
    }
}

3、StringBuilder类添加和反转方法

添加和反转方法:

示例代码:

4、StringBuilder和String相互转换

StringBuilder转换为String:

  • public String toString():通过toString()就可以实现把StringBuilder转换为String

    String转换为StringBuilder:

  • public StringBuilder(String s) :通过构造方法就可以实现把String转换为StringBuilder


3、ArrayList

1、ArrayList类概述

什么是集合:

  • 提供一种存储空间可变的存储模型,存储的数据容量可以发生改变。

    ArrayList集合的特点:

  • 底层是数组实现的,长度可以变化。

泛型的使用:

  • 用于约束集合中存储元素的数据类型。

2、ArrayList类常用方法

构造方法:
| 方法名 | 说明 |
|–|–|
| public ArrayList() | 创建一个空的集合对象 |
成员方法:

public class ArrayListDemo02 {
    public static void main(String[] args) {
        // 创建集合
        ArrayList<String> array = new ArrayList<String>();
        // 添加元素
        array.add("hello");
        array.add("world");
        array.add("java");

        // public boolean remove(Object o):删除指定的元素,返回删除是否成功
        System.out.println(array.remove("world"));
        System.out.println(array.remove("javaee"));

        // public E remove(int index):删除指定索引处的元素,返回被删除的元素
        System.out.println(array.remove(1));

        // IndexOutOfBoundsException
        System.out.println(array.remove(3));

        // public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
        System.out.println(array.set(1, "javaee"));

        // IndexOutOfBoundsException
        System.out.println(array.set(3, "javaee"));

        // public E get(int index):返回指定索引处的元素
        System.out.println(array.get(0));
        System.out.println(array.get(1));
        System.out.println(array.get(2));

        // public int size():返回集合中的元素的个数
        System.out.println(array.size());

        // 输出集合
        System.out.println("array:" + array);
    }
}

4、继承

1、 继承的好处和弊端

继承好处:

  • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
  • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)

继承弊端:

  • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性。

继承的应用场景:

  • 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承。
  • is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类。

2. 继承中的成员访问特点

2.1 继承中变量的访问特点

在子类方法中访问一个变量,采用的是就近原则。

  1. 子类局部范围找
  2. 子类成员范围找
  3. 父类成员范围找
  4. 如果都没有就报错(不考虑父亲的父亲…)

示例代码:

class Fu {
    int num = 10;
}

class Zi {
    int num = 20;
    public void show(){
    int num = 30;
    System.out.println(num);
    }
}

public class Demo1 {
    public static void main(String[] args) {
    Zi z = new Zi();
    z.show(); // 输出show方法中的局部变量30
    }
}

2.2、super:

this&super关键字:

  • this:代表本类对象的引用
  • super:代表父类存储空间的标识(可以理解为父类对象引用)

this和super的使用分别:

2.3 继承中构造方法的访问特点

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法。

子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

  1. 通过使用super关键字去显示的调用父类的带参构造方法
  2. 在父类中自己提供一个无参构造方法
    继承中构造方法的访问特点

2.4、继承中成员方法的访问特点

通过子类对象访问一个方法

  1. 子类成员范围找
  2. 父类成员范围找
  3. 如果都没有就报错(不考虑父亲的父亲…)

2.5、 Java中继承的注意事项

  • Java中类只支持单继承,不支持多继承。

错误范例:class A extends B, C { }

  • Java中类支持多层继承。

多层继承示例代码:

public class Granddad {
    public void drink() {
        System.out.println("爷爷爱喝酒");
    }
}
public class Father extends Granddad {
    public void smoke() {
        System.out.println("爸爸爱抽烟");
    }
}
public class Son extends Father {
    // 此时,Son类中就同时拥有drink方法以及smoke方法
}

继承练习

例: 老师和学生

需求:定义老师类和学生类,然后写代码测试;最后找到老师类和学生类当中的共性内容,抽取出一个父类,用继承的方式改写代码,并进行测试。

class Person {

    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
class Teacher extends Person {
    public Teacher() {}

    public Teacher(String name,int age) {
        super(name,age);//调用父类的有参构造
    }

    public void teach() {
        System.out.println("用爱成就每一位学员");
    }
}

class Student extends Person{
    public Student() {}

    public Student(String name, int age) {
    super(name,age);
    }

    public void study(){
        System.out.println("学生学习");
    }
}
class PersonDemo {
    public static void main(String[] args){
    //创建老师类对象并进行测试
    Teacher t1 = new Teacher();
    t1.setName("林青霞");
    t1.setAge(30);
    System.out.println(t1.getName() + "," + t1.getAge());
    t1.teach();

    Teacher t2 = new Teacher("风清扬", 33);
    System.out.println(t2.getName() + "," + t2.getAge());
    t2.teach();

    // 创建学生类对象测试
    Student s = new Student("张三",23);
    System.out.println(s.getName() + "," + s.getAge());
    s.study();
    }
}

5、修饰符

1、权限修饰符:


2、final

fianl关键字的作用:

  • final代表最终的意思,可以修饰成员方法,成员变量,类。

final修饰类、方法、变量的效果:

  • fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)。
  • final修饰方法:该方法不能被重写。
  • final修饰变量:表明该变量是一个常量,不能再次赋值。

3、static

static的概念:

  • static关键字是静态的意思,可以修饰【成员方法】,【成员变量】。

static修饰的特点:

  1. 被类的所有对象共享,这也是我们判断是否使用静态关键字的条件。
  2. 可以通过类名调用当然,也可以通过对象名调用【推荐使用类名调用】。
class Student {
    public String name; //姓名
    public int age; //年龄
    public static String university; //学校 共享数据!所以设计为静态!
    public void show() {
    System.out.println(name + "," + age + "," + university);
    }
}

public class StaticDemo {
    public static void main(String[] args) {
        // 为对象的共享数据赋值
        Student.university = "传智大学";
        Student s1 = new Student();
        s1.name = "林青霞";
        s1.age = 30;
        s1.show();
        Student s2 = new Student();
        s2.name = "风清扬";
        s2.age = 33;
        s2.show();
    }
}

4、static访问特点

  • 静态成员方法只能访问静态成员【方法或变量】。

    main方法是静态的。


6、多态

1、什么是多态

  • 同一个对象,在不同时刻表现出来的不同形态。

2、多态的前提

  1. 要有继承或实现关系
  2. 要有方法的重写
  3. 要有父类引用指向子类对象

3、多态中的成员访问特点

成员变量:

  • 编译看父类,运行看父类

成员方法:

  • 编译看父类,运行看子类

4、多态的好处和弊端

好处:

  • 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作。

弊端:

  • 不能使用子类的特有成员。

5、多态中的转型

向上转型:

  • 父类引用指向子类对象

向下转型:

  • 格式:子类型 对象名 = (子类型)父类引用;

例:

  • 动物类:
public class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}
  • 猫类:
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
public void playGame() {
    System.out.println("猫捉迷藏");
    }
}
  • 测试类:
public class AnimalDemo {
    public static void main(String[] args) {
        //多态
        //向上转型
        Animal a = new Cat();
        a.eat();
    //  a.playGame();

        //向下转型
        Cat c = (Cat)a;
        c.eat();
        c.playGame();
    }
}

7、抽象类

1、抽象类的概述

  • 当在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
  • 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!

2、抽象类的特点

1、抽象类和抽象方法必须使用 abstract 关键字修饰。

    //抽象类的定义
    public abstract class 类名 {

    //抽象方法的定义
        public abstract void eat();
}

2、抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。

3、抽象类不能实例化

  • 抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态

4、抽象类的子类

  • 要么重写抽象类中的所有抽象方法
  • 要么是抽象类

3、抽象类的成员特点

成员变量:

  • 既可以是变量
  • 也可以是常量

构造方法:

  • 空参构造
  • 有参构造

成员方法:

  • 抽象方法
  • 普通方法

8、接口

1、接口的概述

  • 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
  • Java中的接口更多的体现在对行为的抽象!

2、接口的特点

1、接口用关键字interface修饰

public interface 接口名 {}

2、类实现接口用implements表示

public class 类名 implements 接口名 {}

3、接口不能实例化

  • 接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态

4、 多态的形式:具体类多态,抽象类多态,接口多态。

5、接口的子类

  • 要么重写接口中的所有抽象方法
  • 要么子类也是抽象类

3、抽象类的成员特点

  1. 成员变量:只能是常量且为静态; 默认修饰符:public static final

  2. 构造方法:没有,因为接口主要是扩展功能的,而没有具体存在。

  3. 成员方法:只能是抽象方法。默认修饰符:public abstract


9、内部类

1、内部类的基本使用

1.内部类概念:

  • 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类。

例:

class Outer {
    public class Inner {
    }
}

2、内部类的访问特点:

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象

示例代码:

/*
    内部类访问特点:
        内部类可以直接访问外部类的成员,包括私有
        外部类要访问内部类的成员,必须创建对象
*/
public class Outer {
    private int num = 10;
    public class Inner {
        public void show() {
            System.out.println(num);
        }
    }

    public void method() {
            Inner i = new Inner();
            i.show();
    }
}

2、成员内部类

1.成员内部类的定义位置:

  • 在类中方法,跟成员变量是一个位置

2.外界创建成员内部类格式:

  • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
  • 举例:Outer.Inner oi = new Outer().new Inner();

3.成员内部类的推荐使用方案:

  • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。

3、局部内部类

1.局部内部类定义位置:

  • 局部内部类是在方法中定义的类

2.局部内部类方式方式

  • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用。
  • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量。

示例代码:

class Outer {
    private int num = 10;
    public void method() {
        int num2 = 20;
        class Inner {
            public void show() {
                System.out.println(num);
                System.out.println(num2);
            }
        }
        Inner i = new Inner();
        i.show();
    }
}
public class OuterDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

4、 匿名内部类 (局部内部类的一种):

匿名内部类的前提:

  • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类。

匿名内部类的格式:

  • new 类名 ( ) {
    重写方法
    }

  • new 接口名 ( ) {
    重写方法
    }

举例:

new Inter(){
    @Override
    public void method(){}
};

匿名内部类的本质:

  • 本质:是一个继承了该类或者实现了该接口的子类匿名对象。

匿名内部类直接调用方法:

接口:

interface Inter{
    void method();
}
class Test{
    public static void main(String[] args){
        new Inter(){
            @Override
            public void method(){
                System.out.println("我是匿名内部类");
            }
        }.method(); // 直接调用方法
    }
}

匿名内部类的细节:

  • 匿名内部类可以通过多态的形式接受。
Inter i = new Inter(){
    @Override
    public void method(){
    }
}

匿名内部类在开发中的使用:

  • 当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码

示例代码:

接口:

interface Jumpping {
    void jump();
}

实现类1:

class Cat implements Jumpping {
    @Override
    public void jump() {
        System.out.println("猫可以跳高了");
    }
}

实现类2:

class Dog implements Jumpping {
    @Override
    public void jump() {
        System.out.println("狗可以跳高了");
    }
}

操作类:

class JumppingOperator {
    public void method(Jumpping j) { //new Cat(); new Dog();
        j.jump();
    }
}

测试类:

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

        //需求:创建接口操作类的对象,调用method方法
        JumppingOperator jo = new JumppingOperator();
        Jumpping j = new Cat();
        jo.method(j);

        Jumpping j2 = new Dog();
        jo.method(j2);
        System.out.println("­­­­­­­­");

        // 匿名内部类的简化
        jo.method(new Jumpping() {
            @Override
            public void jump() {
                System.out.println("猫可以跳高了");
            }
        });
        // 匿名内部类的简化
        jo.method(new Jumpping() {
            @Override
            public void jump() {
                System.out.println("狗可以跳高了");
            }
        });
    }
}

9、包装类

1、基本类型包装类

  • 基本类型包装类的作用:将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。

  • 常用的操作之一:用于基本数据类型与字符串之间的转换。

基本类型对应的包装类:


1.2、int和String类型的相互转换

1、int转换为String:

转换方式:

  • 方式一:直接在数字后加一个空字符串
  • 方式二:通过String类静态方法valueOf()

例:

public class IntegerDemo {
    public static void main(String[] args) {
        //int --- String
        int number = 100;

        //方式1
        String s1 = number + "";
        System.out.println(s1);

        //方式2
        //public static String valueOf(int i)
        String s2 = String.valueOf(number);
        System.out.println(s2);
        System.out.println("--------");
    }
}

2、String转换为int:

转换方式:

  • 方式一:先将字符串数字转成Integer,再调用valueOf()方法。
  • 方式二:通过Integer静态方法parseInt()进行转换。

例:

public class IntegerDemo {
    public static void main(String[] args) {
    //String --- int
    String s = "100";

    //方式1:String --- Integer --- int
    Integer i = Integer.valueOf(s);
    //public int intValue()
    int x = i.intValue();
    System.out.println(x);

    //方式2
    //public static int parseInt(String s)
    int y = Integer.parseInt(s);
    System.out.println(y);
    }
}

3、自动拆箱和自动装箱

自动装箱:

  • 把基本数据类型转换为对应的包装类类型

自动拆箱:

  • 把包装类类型转换为对应的基本数据类型

示例代码:

    Integer i = 100; // 自动装箱
    i += 200; // i = i + 200; i + 200 自动拆箱;i = i + 200; 是自动装箱

10、时间日期类

1、 SimpleDateFormat类

SimpleDateFormat类构造方法:


SimpleDateFormat类的常用方法:

  • 格式化(从Date到String):public final String format(Date date):将日期格式化成日期/时间字符串。
  • 解析(从String到Date):public Date parse(String source):从给定字符串的开始解析文本以生成日期。

例:

public class SimpleDateFormatDemo {
    public static void main(String[] args) throws ParseException {
        //格式化:从 Date 到 String
        Date d = new Date();
//             SimpleDateFormat sdf = new SimpleDateFormat();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
        String s = sdf.format(d);
        System.out.println(s);
        System.out.println("--------");

        //从 String 到 Date
        String ss = "2048-08-09 11:11:11";
        //ParseException
        SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date dd = sdf2.parse(ss);
        System.out.println(dd);
    }
}

运行结果:


11、异常

1、异常

异常的概述:

  • 异常就是程序出现了不正常的情况。

异常的体系结构:

Error :严重问题,不需要处理。

Exception:称为异常类,它表示程序本身可以处理的问题。

  • RuntimeException:在编译期是不检查的,出现问题后,需要我们回来修改代码。
  • 非RuntimeException:编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了。

2、Throwable成员方法:

常用方法:


例:

public class ExceptionDemo02 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        try {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]); //new ArrayIndexOutOfBoundsException();
        System.out.println("这里能够访问到吗");
        } catch (ArrayIndexOutOfBoundsException e) { //new ArrayIndexOutOfBoundsException();

        //public String getMessage():返回此 throwable 的详细消息字符串
//           System.out.println(e.getMessage());
        //Index 3 out of bounds for length 3

        //public String toString():返回此可抛出的简短描述
//           System.out.println(e.toString());
        //java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

        //public void printStackTrace():把异常的错误信息输出在控制台:
        e.printStackTrace();
//         java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds
for length 3
//         at com.itheima_02.ExceptionDemo02.method(ExceptionDemo02.java:18)
//         at com.itheima_02.ExceptionDemo02.main(ExceptionDemo02.java:11)

        }
    }
}

3、编译时异常和运行时异常的区别

编译时异常:

  • 都是Exception类及其子类
  • 必须显示处理,否则程序就会发生错误,无法通过编译

运行时异常:

  • 都是RuntimeException类及其子类
  • 无需显示处理,也可以和编译时异常一样处理

4、throws方式处理异常

格式:

    public void 方法() throws 异常类名 {
    }

注意事项:

  1. throws格式是跟在方法的括号后面的。
  2. 编译时异常必须要进行处理,两种处理方案:try…catch …或者 throws,如果采用 throws 这种方案,将来谁调用谁处理
  3. 运行时异常可以不处理,出现问题后,需要我们回来修改代码。

示例代码:

public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("开始");
//             method();
        try {    //谁调用谁处理
            method2();
        }catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println("结束");
    }

    //编译时异常
    public static void method2() throws ParseException {
        String s = "2048-08-09";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date d = sdf.parse(s);
        System.out.println(d);
}

    //运行时异常 可抛可不抛
    public static void method() throws ArrayIndexOutOfBoundsException {
        int[] arr = {1, 2, 3};
        System.out.println(arr[3]);
    }
}

5、自定义异常

例:

  • 自定义异常类:

    public class ScoreException extends Exception {
    
      public ScoreException() {}
    
      public ScoreException(String message) {
          super(message);
      }
    

}

- 老师类:

```java
public class Teacher {
    public void checkScore(int score) throws ScoreException {
        if(score<0 || score>100) {
//             throw new ScoreException();
        throw new ScoreException("你给的分数有误,分数应该在0-100之间");
    } else {
        System.out.println("成绩正常");
        }
    }
}
  • 测试类:
public class Demo {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("请输入分数:");

        int score = sc.nextInt();

        Teacher t = new Teacher();
        try {
            t.checkScore(score);
        } catch (ScoreException e) {
            e.printStackTrace();
        }
    }
}

12、Collection集合

1、集合体系结构

集合类的特点:

  • 提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变

集合类的体系图:

其中:

  • List 为有序可重复的,
  • Set 为无序不可重复的。

2、Collection集合概述和基本使用

Collection集合概述:

  • 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。
  • JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现。

Collection集合的常用方法:

Collection集合基本使用:

public class CollectionDemo01 {
    public static void main(String[] args) {
        //创建Collection集合的对象
        Collection<String> c = new ArrayList<String>();

        //添加元素:boolean add(E e)
        c.add("hello");
        c.add("world");
        c.add("java");

        //输出集合对象
        System.out.println(c);
    }
}

运行结果:


其中ArrayList重写了toString方法,直接输出了集合添加的内容。

3、Collection集合的遍历

lterator:迭代器,集合的专用遍历方式

  • Iterator <E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
  • 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的

lterator中的常用方法:

  • E next():返回迭代中的下一个元素
  • boolean hasNext():如果迭代具有更多元素,则返回true

示例代码:

public class IteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> c = new ArrayList<>();

        //添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
        c.add("javaee");

        //Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
        Iterator<String> it = c.iterator();

        //用while循环改进元素的判断和获取
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
    }
}

输出结果:

13、List集合

1、List集合概述和特点

List集合概述:

  • 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素。
  • 与Set集合不同,列表通常允许重复的元素。

List集合特点:

  1. 有索引
  2. 可以存储重复元素
  3. 元素存取有序

2、List集合的特有方法


3、并发修改异常

出现的原因:

  • 迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值和实际修改值不一致,则会出现:ConcurrentModificationException

解决的方案:

  • 用for循环遍历,然后用集合对象做对应的操作即可。

示例代码:

public class ListDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //遍历集合,得到每一个元素,看有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现
//         Iterator<String> it = list.iterator();
//         while (it.hasNext()) {
//             String s = it.next();
//             if(s.equals("world")) {
//                 list.add("javaee");
//             }
//         }

        for(int i=0; i<list.size(); i++) {
            String s = list.get(i);
            if(s.equals("world")) {
            list.add("javaee");
            }
        }
        //输出集合对象
        System.out.println(list);
    }
}

4、列表迭代器

ListIterator介绍:

  • 通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器。

  • 用于允许程序员沿任一方向遍历的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。

    示例代码:

public class ListIteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //获取列表迭代器
        ListIterator<String> lit = list.listIterator();
        while (lit.hasNext()) {
            String s = lit.next();
            if(s.equals("world")) {
                lit.add("javaee");
            }
        }

        System.out.println(list);
    }
}

5、增强for循环

增强for目的:简化数组和Collection集合的遍历

  • 实现lterable接口的类允许其对象成为增强型for语句的目标。
  • 它是JDK5之后出现的,其内部原理是一个lterator迭代器。

格式:

    for(元素数据类型 变量名 : 数组/集合对象名) {
        循环体;
    }

示例代码:

public class ForDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        for(int i : arr) {
            System.out.println(i);
        }

        System.out.println("--------");

        String[] strArray = {"hello","world","java"};
        for(String s : strArray) {
        System.out.println(s);
        }

        System.out.println("--------");

        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");

        for(String s : list) {
        System.out.println(s);
        }

        System.out.println("--------");
        //内部原理是一个Iterator迭代器
        /*
        for(String s : list) {
        if(s.equals("world")) {
        list.add("javaee"); //ConcurrentModificationException
        }
        }
        */
    }
}

14、List集合的实现类

1、数据结构之数组和链表

数组结构:

  • 查询快、增删慢

队列结构

  • 查询慢、增删快

2、List集合子类的特点

List集合常用子类:ArrayList,LinkedList

  • ArrayList:底层数据结构是数组,查询快,增删慢
  • LinkedList:底层数据结构是链表,查询慢,增删快

3、LinkedList集合的特有功能

特有方法:


15、Set集合

1、Set集合特点

  • 不包含重复元素的集合
  • 没有带索引的方法,所以不能使用普通for循环遍历

2、哈希值

哈希值简介:

  • 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。

如何获取哈希值:

  • Object类中的public int hashCode():返回对象的哈希码值。

哈希值的特点:

  • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
  • 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同。

3、HashSet集合概述和特点

HashSet集合的特点:

  • 底层数据结构是哈希表
  • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
  • 没有带索引的方法,所以不能使用普通for循环遍历
  • 由于是Set集合,所以是不包含重复元素的集合

HashSet集合的基本使用:

public class HashSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        HashSet<String> hs = new HashSet<String>();

        //添加元素
        hs.add("hello");
        hs.add("world");
        hs.add("java");

        hs.add("world");
        //遍历
        for(String s : hs) {
            System.out.println(s);
        }
    }
}

运行结果:

4、HashSet集合存储学生对象并遍历

案例需求 :

  • 创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合。
  • 要求:学生对象的成员变量值相同,我们就认为是同一个对象。

代码实现:

  • 学生类

    public class Student {
      private String name;
      private int age;
    
      public Student() {
      }
    
      public Student(String name, int age) {
          this.name = name;
          this.age = age;
      }
    
      public String getName() {
          return name;
      }
    
      public void setName(String name) {
          this.name = name;
      }
    
      public int getAge() {
          return age;
      }
    
      public void setAge(int age) {
          this.age = age;
      }
    
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (o == null || getClass() != o.getClass()) return false;
          Student student = (Student) o;
          if (age != student.age) return false;
          return name != null ? name.equals(student.name) : student.name ==
          null;
      }
    
      @Override
      public int hashCode() {
          int result = name != null ? name.hashCode() : 0;
          result = 31 * result + age;
          return result;
      }
    }
  • 测试类:

public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);
        Student s4 = new Student("王祖贤", 33);

        //把学生添加到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //遍历集合(增强for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

16、Set集合排序

1、TreeSet集合概述和特点

TreeSet集合概述:

  1. 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法。

         TreeSet():根据其元素的自然排序进行排序。

         TreeSet(Comparator comparator) :根据指定的比较器进行排序。

  1. 没有带索引的方法,所以不能使用普通for循环遍历。
  2. 由于是Set集合,所以不包含重复元素的集合。

TreeSet集合基本使用:

public class TreeSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>();

        //添加元素
        ts.add(10);
        ts.add(40);
        ts.add(30);
        ts.add(50);
        ts.add(20);

        ts.add(30);

        //遍历集合
        for(Integer i : ts) {
            System.out.println(i);
        }
    }
}

运行结果:

2、自然排序Comparable

案例需求:

  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序

代码实现:

  • 学生类:
public class Student implements Comparable<Student> {

    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public int compareTo(Student s) {
        // return 0;
        // return 1;
        // return -1;
        //按照年龄从小到大排序
        int num = this.age - s.age;
        // int num = s.age - this.age;
        //年龄相同时,按照姓名的字母顺序排序
        int num2 = num==0?this.name.compareTo(s.name):num;
        return num2;
    }
}
  • 测试类:
public class TreeSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);
        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

3、比较器排序Comparator

案例需求:

  • 存储学生对象并遍历,创建TreeSet集合使用带参构造方法。
  • 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序。

代码实现:

  • 学生类:
public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • 测试类
public class TreeSetDemo {
    public static void main(String[] args) {
    //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
        @Override
        public int compare(Student s1, Student s2) {
        //this.age - s.age
        //s1,s2
        int num = s1.getAge() - s2.getAge();
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()): num;
        return num2;
        }
    });

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);
        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

17、泛型

1、泛型类

定义格式:

修饰符 class 类名<类型> { }

此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

示例代码:

泛型类:

public class Generic<T> {
    private T t;

    public T getT() {
        return t;
    }
    public void setT(T t) {
        this.t = t;
    }
}

测试类:

public class GenericDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new Generic<String>();
        g1.setT("林青霞");
        System.out.println(g1.getT());

        Generic<Integer> g2 = new Generic<Integer>();
        g2.setT(30);
        System.out.println(g2.getT());

        Generic<Boolean> g3 = new Generic<Boolean>();
        g3.setT(true);
        System.out.println(g3.getT());
    }
}

2、泛型方法

定义格式:

修饰符 <类型> 返回值类型 方法名(类型 变量名) { }

示例代码:

带有泛型方法的类:

public class Generic {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

测试类:

public class GenericDemo {
    public static void main(String[] args) {
        Generic g = new Generic();
        g.show("林青霞");
        g.show(30);
        g.show(true);
        g.show(12.34);
    }
}

3、泛型接口

定义格式:

修饰符 interface 接口名<类型> { }

示例代码:

泛型接口:

public interface Generic<T> {
    void show(T t);
}

泛型接口实现类:

public class GenericImpl<T> implements Generic<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
    }
}

测试类:

public class GenericDemo {
    public static void main(String[] args) {
        Generic<String> g1 = new GenericImpl<String>();
        g1.show("林青霞");
        Generic<Integer> g2 = new GenericImpl<Integer>();
        g2.show(30);
    }
}

18、可变参数

可变参数介绍:

  • 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了。

可变参数定义格式:

 修饰符 返回值类型 方法名(数据类型… 变量名) { }

可变参数的注意事项:

  • 这里的变量其实是一个数组
  • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后

可变参数的基本使用:

19、Map集合

1、Map集合概述和特点

Map集合概述:

interface Map<K,V> K:键的类型;V:值的类型

Map集合的特点:

  1. 键值对映射关系
  2. 一个键对应一个值
  3. 键不能重复,值可以重复
  4. 元素存取无序

Map集合的基本使用:

public class MapDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String,String> map = new HashMap<String,String>();
        //V put(K key, V value) 将指定的值与该映射中的指定键相关联
        map.put("itheima001","林青霞");
        map.put("itheima002","张曼玉");
        map.put("itheima003","王祖贤");

        map.put("itheima003","柳岩");//键值相同时会覆盖原来的元素

        //输出集合对象
        System.out.println(map);
    }
}

运行结果:

2、Map集合的基本功能

方法介绍:

3、Map集合的获取功能

4、Map集合的遍历

方式一:

步骤分析:

  1. 获取所有键的集合。用keySet()方法实现
  2. 遍历键的集合,获取到每一个键。用增强for实现
  3. 根据键去找值。用get(Object key)方法实现

代码实现:

public class MapDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //获取所有键的集合。用keySet()方法实现
        Set<String> keySet = map.keySet();
        //遍历键的集合,获取到每一个键。用增强for实现
        for (String key : keySet) {
            //根据键去找值。用get(Object key)方法实现
            String value = map.get(key);
            System.out.println(key + "," + value);
        }
    }
}

方式二:

步骤分析:

  1. 获取所有键值对对象的集合
    Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合

  2. 遍历键值对对象的集合,得到每一个键值对对象
    用增强for实现,得到每一个Map.Entry

  3. 根据键值对对象获取键和值
    getKey()得到键
    getValue()得到值

例:

public class MapDemo02 {
    public static void main(String[] args) {
            //创建集合对象
    Map<String, String> map = new HashMap<String, String>();

        //添加元素
        map.put("张无忌", "赵敏");
        map.put("郭靖", "黄蓉");
        map.put("杨过", "小龙女");

        //获取所有键值对对象的集合
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        //遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> me : entrySet) {
            //根据键值对对象获取键和值
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "," + value);
        }
    }
}

20、Collections集合工具类

1、Collections概述和使用

Collections类的作用:

  • 是针对集合操作的工具类

Collections类常用方法:


21、File类

1、File类概述和构造方法

File类介绍:

  • 它是文件和目录路径名的抽象表示
  • 文件和目录可以通过File封装成对象
  • 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的。

File类的构造方法:

示例代码:

public class FileDemo01 {
    public static void main(String[] args) {
        //File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
        File f1 = new File("E:\\itcast\\java.txt");
        System.out.println(f1);

        //File(String parent, String child):从父路径名字符串和子路径名字符串创建新的File实例。
        File f2 = new File("E:\\itcast","java.txt");
        System.out.println(f2);

        //File(File parent, String child):从父抽象路径名和子路径名字符串创建新的 File实例。
        File f3 = new File("E:\\itcast");
        File f4 = new File(f3,"java.txt");
        System.out.println(f4);
    }
}

其中File类重写了toString方法。

运行结果:

2、File类创建功能

方法分类:


示例代码:

public class FileDemo02 {
    public static void main(String[] args) throws IOException {
        //需求1:我要在E:\\itcast目录下创建一个文件java.txt
        File f1 = new File("E:\\itcast\\java.txt");
        System.out.println(f1.createNewFile());//如果文件原本不存在,就创建文件,返回true,如果文件存在,不创建文件,返回false
        System.out.println("--------");

        //需求2:我要在E:\\itcast目录下创建一个目录JavaSE
        File f2 = new File("E:\\itcast\\JavaSE");
        System.out.println(f2.mkdir());//如果目录原本不存在,就创建目录,返回true;如果目录存在,不创建目录,返回false
        System.out.println("--------");

        //需求3:我要在E:\\itcast目录下创建一个多级目录JavaWEB\\HTML
        File f3 = new File("E:\\itcast\\JavaWEB\\HTML");
//         System.out.println(f3.mkdir());
        System.out.println(f3.mkdirs());
        System.out.println("--------");

        //需求4:我要在E:\\itcast目录下创建一个文件javase.txt
        File f4 = new File("E:\\itcast\\javase.txt");
//         System.out.println(f4.mkdir());//会创建目录,而不是文件。
        System.out.println(f4.createNewFile());
    }
}

3、File类判断和获取功能

判断功能:


获取功能:

4、File类删除功能


删除目录时的注意事项:

  • 如果一个目录中有内容(目录,文件),不能直接删除。应该先删除目录中的内容,最后才能删除目录

22、IO流

1、IO流的分类

按照数据类型来分:

1.字节流:

  • 字节输入流
  • 字节输出流

2.字符流:

  • 字符输入流
  • 字符输出流

IO流的使用场景;

  • 如果操作的是纯文本文件,优先使用字符流。
  • 如果操作的是图片、视频、音频等二进制文件。优先使用字节流。
  • 如果不确定文件类型,优先使用字节流。字节流是万能的流。

2、字节流写数据:

字节流抽象基类:

  • InputStream:这个抽象类是表示字节输入流的所有类的超类
  • OutputStream:这个抽象类是表示字节输出流的所有类的超类

子类名特点:子类名称都是以其父类名作为子类名的后缀

字节输出流:

  • FileOutputStream(String name):创建文件输出流以指定的名称写入文件。

使用字节输出流写数据的步骤:

  1. 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
  2. 调用字节输出流对象的写数据方法
  3. 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)

示例代码:

public class FileOutputStreamDemo01 {
    public static void main(String[] args) throws IOException {

        //创建字节输出流对象
        //FileOutputStream(String name):创建文件输出流以指定的名称写入文件
        FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");

        //void write(int b):将指定的字节写入此文件输出流
        fos.write(97);
        // fos.write(57);
        // fos.write(55);

        //最后都要释放资源
        //void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
        fos.close();
    }
}

3、字节流写数据的三种方式


4、字节流写数据加异常处理

    try{
        可能出现异常的代码;
    }catch(异常类名 变量名){
        异常的处理代码;
    }finally{
        执行所有清除操作;
    }

finally特点:

  • 被finally控制的语句一定会执行,除非JVM退出。

示例代码:

public class FileOutputStreamDemo04 {
    public static void main(String[] args) {
        //加入finally来实现释放资源
        FileOutputStream fos = null;//初始化,保证fos.close能执行。
        try {
            fos = new FileOutputStream("myByteStream\\fos.txt");
            fos.write("hello".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

5、字节流读数据:

1、一次读一个字节数据:

字节输入流:

  • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文件系统中的路径名name命名。

示例代码:

public class FileInputStreamDemo01 {
    public static void main(String[] args) throws IOException {
        //创建字节输入流对象
        //FileInputStream(String name)
        FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");

        int by;
        /*
        fis.read():读数据
        by=fis.read():把读取到的数据赋值给by
        by != -1:判断读取到的数据是否是-1
        */
        while ((by=fis.read())!=-1) {
            System.out.print((char)by);
        }

        //释放资源
        fis.close();
    }
}

2、一次读一个字节数组数据:

一次读一个字节数组的方法:

  • public int read(byte[] b):从输入流读取最多b.length个字节的数据

示例代码:

public class FileInputStreamDemo02 {
    public static void main(String[] args) throws IOException {
        //创建字节输入流对象
        FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");

        /*
            hello\r\n
            world\r\n

            第一次:hello
            第二次:\r\nwor
            第三次:ld\r\nr

        */

        byte[] bys = new byte[1024]; //1024及其整数倍
        int len;
        while ((len=fis.read(bys))!=-1) {
            System.out.print(new String(bys,0,len));
        }

        //释放资源
        fis.close();
    }
}

6、字节流复制文本文件

案例需求

  • 把“E:\itcast\窗里窗外.txt”复制到模块目录下的“窗里窗外.txt”

实现步骤:

  • 复制文本文件,其实就把文本文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)

  • 数据源::
    E:\itcast\窗里窗外.txt — 读数据 — InputStream — FileInputStream

  • 目的地:
    myByteStream\窗里窗外.txt — 写数据 — OutputStream — FileOutputStream

代码实现:

public class CopyTxtDemo {
    public static void main(String[] args) throws IOException {
        //根据数据源创建字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\itcast\\窗里窗外.txt");
        //根据目的地创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("myByteStream\\窗里窗外.txt");

        //读写数据,复制文本文件(一次读取一个字节,一次写入一个字节)
        int by;
        while ((by=fis.read())!=-1) {
            fos.write(by);
        }

        //释放资源
        fos.close();
        fis.close();
    }
}

7、字节流复制图片

案例需求:

  • 把“E:\itcast\mn.jpg”复制到模块目录下的“mn.jpg”

实现步骤:

  1. 根据数据源创建字节输入流对象。
  2. 根据目的地创建字节输出流对象。
  3. 读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
  4. 释放资源

代码实现:

public class CopyJpgDemo {
    public static void main(String[] args) throws IOException {
        //根据数据源创建字节输入流对象
        FileInputStream fis = new FileInputStream("E:\\itcast\\mn.jpg");
        //根据目的地创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("myByteStream\\mn.jpg");

        //读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
        byte[] bys = new byte[1024];
        int len;
        while ((len=fis.read(bys))!=-1) {
            fos.write(bys,0,len);
        }

        //释放资源
        fos.close();
        fis.close();
    }
}

23、字节缓冲流

1、字节缓冲流构造方法

字节缓冲流构造方法:

  • BufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。

  • BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。

构造方法:


示例代码:

public class BufferStreamDemo {
    public static void main(String[] args) throws IOException {
        //字节缓冲输出流:BufferedOutputStream(OutputStream out)

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\bos.txt"));
        //写数据
        bos.write("hello\r\n".getBytes());
        bos.write("world\r\n".getBytes());
        //释放资源
        bos.close();

        //字节缓冲输入流:BufferedInputStream(InputStream in)
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myByteStream\\bos.txt"));

        //一次读取一个字节数据
        // int by;
        // while ((by=bis.read())!=-1) {
        // System.out.print((char)by);
        // }

        //一次读取一个字节数组数据
        byte[] bys = new byte[1024];
        int len;
        while ((len=bis.read(bys))!=-1) {
            System.out.print(new String(bys,0,len));
        }

        //释放资源
        bis.close();
    }
}

24、字符流

1、为什么会出现字符流

字符流的介绍:

  • 由于字节流操作中文不是特别的方便,所以Java就提供字符流
    字符流 = 字节流 + 编码表

中文的字节存储方式:

  • 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文。

2、字符串中的编码解码问题

相关方法:


代码演示:

public class StringDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //定义一个字符串
        String s = "中国";

        //byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
        byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
        System.out.println(Arrays.toString(bys));

        //String ss = new String(bys);
        //String ss = new String(bys,"UTF-8");
        String ss = new String(bys,"GBK");
        System.out.println(ss);
    }
}

3、字符流中的编码解码问题

字符流中和编码解码的两个类:

  • InputStreamReader:是从字节流到字符流的桥梁,它读取字节,并使用指定的编码将其解码为字符
    它使用的字符集可以由名称指定,也可以接受平台的默认字符集。

  • OutputStreamWriter:是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节它使用的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法:


示例代码:

public class ConversionStreamDemo {
    public static void main(String[] args) throws IOException {
        //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"),"GBK");
        osw.write("中国");
        osw.close();

        //InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"));
        InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"),"GBK");

        //一次读取一个字符数据
        int ch;
        while ((ch=isr.read())!=-1) {
            System.out.print((char)ch);
        }
        isr.close();
    }
}

4、字符流写数据的5种方式

方法介绍:


刷新和关闭的方法:


刷新流作用:刷新缓冲,写入数据。

5、字符流读数据的2种方式


6、字符缓冲流

构造方法:


7、字符缓冲流特有功能

BufferedWriter:

写一个换行符,由操作系统系统决定。

BufferedReader:


8、字符缓冲流特有功能复制Java文件

代码实现:

public class CopyJavaDemo02 {
    public static void main(String[] args) throws IOException {
        //根据数据源创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
        //根据目的地创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));

        //读写数据,复制文件
        //使用字符缓冲流特有功能实现
        String line;
        while ((line=br.readLine())!=null) {
            bw.write(line);
            bw.newLine();//换行
            bw.flush();
        }
        //释放资源
        bw.close();
        br.close();
    }
}

9、IO流小结

字节流:

字节流可以复制任意文件数据,一般采用字节缓冲流一次读写一个字节数组的方式

字符流:

字符流只能复制文本数据,一般使用字符缓冲流。

25、IO特殊操作流

1、标准输入流

System 类中有两个静态的成员变量

  • public static final InputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源。(InputStream是返回值类型 in是方法,可以直接由类直接调用)

  • public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标。

自己实现键盘录入数据:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

写起来太麻烦,Java就提供了一个类实现键盘录入:

Scanner sc = new Scanner(System.in);

2、对象序列化流

对象序列化介绍:

  • 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象;

  • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。

  • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息;反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

对象序列化流: ObjectOutputStream

  • 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。

构造方法:

序列化对象的方法:


示例代码:

学生类

public class Student implements Serializable {
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

测试类:

public class ObjectOutputStreamDemo {
    public static void main(String[] args) throws IOException {

    //ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));

    //创建对象
    Student s = new Student("林青霞",30);

    //void writeObject(Object obj):将指定的对象写入ObjectOutputStream
    oos.writeObject(s);

    //释放资源
    oos.close();
    }
}

注意事项:

  1. 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
  2. Serializable是一个标记接口,实现该接口,不需要重写任何方法

3、对象反序列化流

对象反序列化流: ObjectInputStream

  • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象

构造方法:


反序列化对象的方法:


示例代码:

public class ObjectInputStreamDemo {
    public static void main(String[] args) throws IOException,ClassNotFoundException {
    //ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));

    //Object readObject():从ObjectInputStream读取一个对象
    Object obj = ois.readObject();

    Student s = (Student) obj;
    System.out.println(s.getName() + "," + s.getAge());

    ois.close();
    }
}

26、多线程

1、进程和线程

进程:是正在运行的程序

  • 是系统进行资源分配和调用的独立单位
  • 每一个进程都有它自己的内存空间和系统资源

线程:是进程中的单个顺序控制流,是一条执行路径

  • 单线程:一个进程如果只有一条执行路径,则称为单线程程序
  • 多线程:一个进程如果有多条执行路径,则称为多线程程序

2、实现多线程方式一:继承Thread类

方法介绍:


实现步骤:

  1. 定义一个类MyThread继承Thread类
  2. 在MyThread类中重写run()方法
  3. 创建MyThread类的对象
  4. 启动线程

例:

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {

        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread();

        //void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法
        my1.start();
        my2.start();
    }
}

两个小问题:

为什么要重写run()方法?

  • 因为run()是用来封装被线程执行的代码

run()方法和start()方法的区别?

  • run():封装线程执行的代码,直接调用,相当于普通方法的调用
  • start():启动线程;然后由JVM调用此线程的run()方法

3、设置和获取线程名称


例:

获取main方法的线程名

public class ThreadTest {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
    }
}

4、线程优先级

线程调度:

两种调度方式

  • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
  • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。(Java使用的是抢占式调度模型)

优先级相关方法:


默认优先级为5

5、线程控制

相关方法:


6、线程的生命周期

7、实现多线程方式二:实现Runnable接口

Thread构造方法:


实现步骤:

  1. 定义一个类MyRunnable实现Runnable接口
  2. 在MyRunnable类中重写run()方法
  3. 创建MyRunnable类的对象
  4. 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
  5. 启动线程

例:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}
public class MyRunnableDemo {
    public static void main(String[] args) {
        //创建MyRunnable类的对象
        MyRunnable my = new MyRunnable();

        //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
        //Thread(Runnable target)
        // Thread t1 = new Thread(my);
        // Thread t2 = new Thread(my);

        //Thread(Runnable target, String name)
        Thread t1 = new Thread(my,"高铁");
        Thread t2 = new Thread(my,"飞机");
        //启动线程
        t1.start();
        t2.start();
    }
}

总结:

多线程的实现方案有两种:

  • 继承Thread类
  • 实现Runnable接口

相比继承Thread类,实现Runnable接口的好处:

  • 避免了Java单继承的局限性
  • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想。

🆗,关于java的基础知识总结就到这里了,后面会接着学习Java EE 的 SpringMVC+Mybates框架。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 2058751973@qq.com

×

喜欢就点赞,疼爱就打赏