Bootstrap

JavaSE概念详解,代码事例,基础,IO,网络,Lambda,反射,模块化,注解,XML解析

数据类型

基础类型

int num1 = 10;
byte num2 = 20;
short num3 = 30;
long num4 = 30L; // Long类型在数字类型加个L
// 浮点数
float num5 = 50.1F;
double num6 = 3.1415926;

// 字符
char name = 'a';
String names = "hello"; // String 不是关键字
// 布尔值
boolean flag = true;

在这里插入图片描述

数组

int [] arr = new int[2];
arr[0] = 0;
arr[1] = 1;
System.out.println(arr.length);
System.out.print(arr[0]);
System.out.print(arr[1]);

int [] arr2 = { 1, 2 };

方法

public class Fun {
    public  static void main(String[] args) {
        isEvenNumber(10);
        System.out.println(add(100, 200));
    }
    public static void isEvenNumber(int num) {
        System.out.println(num % 2 == 0);
    }
    public static int add(int a, int b) {
        int sum = a + b;
        return sum;
    }
}

方法重载

  • 多个方法在同一个类中
  • 多个方法具有相同的方法名
  • 多个方法的参数不相同,类型或数量
public class Fun {
    public  static void main(String[] args) {
        System.out.println(add(1, 2)); // 3
        System.out.println(add(1.1, 2.2)); // 3.3000000000000003
        System.out.println(add(1, 2, 3)); // 6
    }

    public static int add(int a, int b) {
        return a + b;
    }
    public static double add(double a, double b) {
        return a + b;
    }
    public static int add(int a, int b, int c) {
        return a + b + c;
    }
    // 可变参数
    public static int add(int... s) {
        int sum = 0;
        for (int i = 0;i < s.length; i ++) {
            sum += s[i];
        }
        return sum;
    }
}

重载: 参数不同,于返回值无关

类和对象

package demo01;

public class Phone {
    String phoneCode;
    int price;
    public String PhoneCode;

    public void call() {
        System.out.println("给" + this.getPhoneCode() + "打电话!");
    }
    public void sendMsg() {
        System.out.println("给" + this.getPhoneCode() + "发短信!");
    }
    public String getPhoneCode() {
        return this.PhoneCode;
    }
}

对象

public class PhoneDeom {
    public  static void main(String[] args) {
        Phone phone = new Phone();
        phone.PhoneCode = "13800000000";
        phone.call();
        phone.sendMsg();
    }
}

两个对象指向不同内存地址

Phone phone = new Phone();
phone.PhoneCode = "13800000000";

Phone phone2 = new Phone();
phone2.PhoneCode = "13800000002";

两个对象指向相同地址

Phone phone = new Phone();
phone.PhoneCode = "13800000000";

Phone phone2 = phone;
phone2.PhoneCode = "13800000002";

成员变量和局部变量

public class Phone {
    int price; // 成员变量 (类中方法外的变量)

    public void call() {
        int price; // 局部变量 (类内的变量)
    }
}

封装

private

  • 可以修饰变量 和 方法
  • 被修饰的变量 或 方法,只有在本类中才能访问
public class Student {
    private String name;

    public void setName(String n) {
        name = n;
    }
    public String getName() {
        return name;
    }
}
public class StudentDemo {
    public  static void main(String[] args) {
        Student student = new Student();
        student.setName("张三");
        String name = student.getName();
        System.out.println(name);
    }
}

构造方法

public class ConstructionMethod {
    private int a;
    private int b;
    public ConstructionMethod() {
        System.out.println("无参构造方法");
    }
    public ConstructionMethod(int a, int b) {
        this.a = a;
        this.b = b;
    }
    public int add () {
        return a + b;
    }
}
public class ConstructionMethodDemo {
    public  static void main(String[] args) {
        ConstructionMethod constructionMethod = new ConstructionMethod(9, 9);
        int sum = constructionMethod.add();
        System.out.println(sum); // 18
    }
}

String构造方法

String s1 = new String();
System.out.println("s1" + s1);

char [] ch = { 'a', 'b', 'c' };
String s2 = new String(ch);
System.out.println("s2" + s2);

byte[] bys = { 97, 98, 99 };
String s3 = new String(bys);
System.out.println("s3" + s3);
equals 比较字符串相等
char [] chs = {'A', 'B', 'C'};
String str0 = new String(chs);
String str1 = new String(chs);
String str2 = "Abc";
String str3 = "Abc";
System.out.println(str0 == str1); // false
System.out.println(str0.equals(str1)); // true
System.out.println(str3 == str2); // true
str3.charAt(1) // b 字符串取值

StringBuilder

StringBuilder s1 = new StringBuilder();
StringBuilder s2 = s1.append("hello");
// 添加
s1.append("6666");
s2.append(8888).append("9999").append("----");
System.out.println(s1); // hello666688889999----
System.out.println(s2); // hello666688889999----
System.out.println(s1 == s1);
// 反转
s2.reverse();
System.out.println(s1); // ----999988886666olleh
System.out.println(s2); // ----999988886666olleh

StringBuilder String 换转

String str = s1.toString();
System.out.println(str);
StringBuilder strB = new StringBuilder(str);
System.out.println(strB);

集合

ArrayList<String> arr = new ArrayList<>();

arr.add("hello");
System.out.println(arr); // [hello]

arr.add("hello2");
System.out.println(arr); // [hello, hello2]

arr.add(1, "-hello-");
System.out.println(arr); // [hello, -hello-, hello2]

System.out.println(arr.remove("-hello-")); // true
System.out.println(arr); // [hello, hello2]

arr.remove(1);
System.out.println(arr); // [hello]

arr.set(0, "ttthhhh");
System.out.println(arr); // [ttthhhh]

System.out.println(arr.get(0)); // ttthhhh

System.out.println(arr.size()); // 1

遍历

for (int i = 0; i < arr.size(); i ++) {
    System.out.println(arr.get(i));
}

Demo

package arrList;

public class Student {
    private String name;
    private int age;

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

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }
}
package arrList;

import java.util.ArrayList;

public class StudentDemo {
    public  static void main(String[] args) {
        ArrayList<Student> students = new ArrayList<>();
        Student student1 = new Student("ZhangSan", 18);
        Student student2 = new Student("LiSi", 19);
        Student student3 = new Student("WangWu", 20);
        students.add(student1);
        students.add(student2);
        students.add(student3);

        for(int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println(s.getName() + "," + s.getAge());
                                // ZhangSan,18  // LiSi,19 // WangWu,20
        }
    }
}

改进

package arrList;

import java.util.ArrayList;

public class StudentDemo {
    public  static void main(String[] args) {
        ArrayList<Student> students = new ArrayList<>();
        addStudent("ZhangSan", 18, students);
        addStudent("LiSi", 19, students);
        addStudent("WangWu", 20, students);

        for(int i = 0; i < students.size(); i++) {
            Student s = students.get(i);
            System.out.println(s.getName() + "," + s.getAge());
                                // ZhangSan,18  // LiSi,19 // WangWu,20
        }
    }

    public static void addStudent(String name, int age, ArrayList<Student> arr) {
        Student student = new Student(name, age);
        arr.add(student);
    }
}

继承

可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法

package extend;

public class Parent {
    public String name = "hello";

    public void show() {
        System.out.println("SHOW HELLO!");
    }
}
package extend;

public class Child extends Parent {
}
package extend;

public class Demo {
    public  static void main(String[] args) {
        Child child = new Child();
        child.show();
        System.out.println(child.name);
    }
}

super

package extend;

public class Parent {
    public String name = "Parent";
}
package extend;

public class Child extends Parent {
    public String name = "Child";

    public void show() {
        String name = "Show Fn";
        System.out.println(name); // Show Fn
        System.out.println(this.name); // Child
        System.out.println(super.name); // Parent
    }
}
package extend;

public class Demo {
    public  static void main(String[] args) {
        Child child = new Child();
        child.show();
    }
}
继承中构造方法的访问特点
  • 因为子类会继承子类中的数据,可能还会使用父类中的数据。所以,之类在初始化之前,一定要父类先完成初始化;
  • 每个子类构造方法的第一条语句都是 super()
super(); // 有参、无参构造方法

调用父类中的工作方法

重写

package extend;

public class Parent {
    public void hello(String name) {
        System.out.println(name + ",你好!");
    }
}
package extend;

public class Child extends Parent {
    public void hello(String name) {
        super.hello(name);
        System.out.println(name + ",吃了吗,你嘞!");
    }
}
package extend;

public class Demo {
    public  static void main(String[] args) {
        Child child = new Child();
        child.hello("阿卜杜拉沙拉木");
    }
}

私有方法不能被重写(父类私有成员,子类不能继承的)

子类方法访问权限不能更底(public > 默认 > private )

修饰符

包 package

其实就是文件夹,对类进行分类管理

格式 : package 报名;(多级包,用"."分开,如:package com.blm.dto;)

导包 import

import arrList.Student;

修饰符

在这里插入图片描述

状态修饰符

final
  • 修饰方法:表明是最终方法,不能被重写
  • 修饰变量:表明是常量,不能再次赋值
  • 修饰类:表明改类是最终类,不能被继承
public void hello(String name) {
    
}
// final修饰,不能重写
public final hello(String name) {
    
}
public final class  Parent {
    public final int a = 1;
}
static
package demo02;

public class Static {
    public static String className;
    public String name;
    public int age;

    public void show () {
        System.out.println(className + "," + name + "," + age + "岁");
    }
}
package demo02;

public class StaticDemo {
    public  static void main(String[] args) {
        Static.className = "计算机科学与技术1班";

        Static s = new Static();
//        s.className = "计算机科学与技术1班";  // 不建议
        s.name = "詹姆斯";
        s.age = 35;
        s.show();

        Static s2 = new Static();
        s2.name = "乔丹";
        s2.age = 65;
        s2.show();
    }
}
static访问特点

静态成员方法,只能访问静态成员方法

非静态的成员方法

  • 能访问静态的成员方法和变量
  • 能访问非静态的成员成员方法和变量

静态的成员方法

  • 只能访问静态的成员方法和变量

多态

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

多态的前提和提现

  • 有继承 / 实现关系
  • 有方法重写
  • 有分类引用指向子类对象
package multimode;

public class Animal {
    public int age = 18;
    public void eat() {
        System.out.println("动物吃东西!");
    }
}
package multimode;

public class Dog extends Animal {
    public int age = 19;
    public void eat() {
        System.out.println("小狗吃肉!");
    }
}
package multimode;

public class Demo {
    public  static void main(String[] args) {
        Animal a = new Dog(); // 多态
        a.eat();
        System.out.println(a.age);
    }
}

多态访问特点:

**成员变量:**编译看左边,运行看左边;

**成员方法:**编译看左边,执行看右边;

因为:成员方法有重写,成员变量没有;

抽象类 abstract

package abstract_demo;

public abstract class Animal { // 抽象类
    public abstract void eat(); // 抽象方法
}
  • 抽象类和抽象方法必须使用abstract关键字修饰
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 抽象类不能实例化,
    • 参照多肽的方式,通过子类对象实例化,这叫抽象类多态
  • 抽象类的子类
    • 要么重写抽象类中的所有抽象方法
    • 要么是抽象类
package abstract_demo;

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

抽象类成员的特点

  • 成员变量(变量、常量)

  • 构造方法
    有构造方法不能实例化;用于之类访问父类数据初始化

  • 成员方法
    可以有抽象方法:限定之类必须完成某些动作
    也可以有非抽象方法:提高代码复用性

接口

特点

  • 接口用关键字implement修饰
  • 类实现接口用 implements 表示
  • 接口不能实例化
    接口要参照多态的方式,通过实现类对象实例化,这叫接口多态
    多态的形式:具体类多态,抽象类多态接口多态
    多态的前提:有继承或者实现关系;有方法重写;有父(类 /接口)引用指向(子/ 实现)类对象
package interface_demo;

public interface Jumpping { // 定义接口
    public abstract void jump();
}
package interface_demo;

public class Cat implements Jumpping {
    public void jump() {
        System.out.println("猫跳高");
    }
}
package interface_demo;

public abstract class Dag implements Jumpping {
}
package interface_demo;

public class Demo {
    public static void main(String[] args) {
        Jumpping j = new Cat();
        j.jump();
    }
}

接口的成特点

  • 成员变量:只能是常量(默认修饰:static final)

    package interface_demo;
    
    public interface Jumpping { // 定义接口
        public int num1 = 10;
        public static final int num2 = 10;
        // num1 num2 等价
    }
    
  • 构造方法
    接口没有构造方法,因为接口主要是对行为进行抽象的,没有具体操作
    一个类如果没有父类,默认继承自Object类

    package interface_demo;
    
    public abstract class Dag extends Object implements Jumpping {
    }
    
  • 成员方法
    只能是抽象方法(默认修饰:public abstract)

    public interface Jumpping { // 定义接口
        public abstract void jump();
        void jump2();
    }
    

类和接口的关系

实现类可以有多接口,或再继承一个类

public class InterImpl extends Object implements Inter1, Inter2, Inter3 {}

接口可以继承多个接口

public interface Inter1 extends Inter2, Inter3 {}

抽象类接口的区别

  • 成员区别

    抽象类变量,常量;构造方法;抽象、非抽象方法
    接口常量;抽象方法
  • 关系区别

    类与类继承,单继承
    类与接口实现、单实现、多实现
    接口于接口继承、单继承、多继承
  • 设计理念的区别

    抽象类对类抽象,抽象属性行为
    接口对行为抽象,主要是行为

内部类

内部类访问特点

  • 内部类可以直接访问尾部类的成员,包括私有成员
  • 外部类访问内部类的成员,必须创建对象,然后也可以访问私有成员
package inside;

public class A {
    private String name1 = "helloA";
    public class B {
        private String name = "helloB";
        public void show () {
            System.out.println(name1);
        }
    }

    public void show () {
        B b = new B();
        b.show();
        System.out.println(b.name);
    }
}

成员内部类

  • 在类的成员位置:成员内部类

    package inside;
    
    public class A {
        public class B {
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            A.B ab = new A().new B();
            // 内部类对象(不常见,内部类目的是,不让外部直接访问)
        }
    }
    
  • 在类的局部位置:局部内部类

package inside;

public class A {
    private String name1 = "helloA";
    public void method () {
      class B {
          public void show () {
              System.out.println(name1);
          }
      }
      B b = new B();
      b.show();
    }
}

public class Demo {
    public static void main(String[] args) {
        A a = new A();
        a.method();
    }
}

局部内部类是在方法中定义的类,所以外界是无法直接使用的,需要内部创建对象

内部类可以直接访问外部类的成员、方法内的局部变量

匿名内部类

package inside;

public interface I {
    void show();
}
package inside;

public class A {
    private String name1 = "helloA";
    public void method () {
       /**
        new I() {
            public void show() {
                System.out.println("内部类");
            }
        }.show();
        */
        I i =  new I() {
            public void show() {
                System.out.println("内部类");
            }
        };
        i.show();
    }
}
package inside;

public class Demo {
    public static void main(String[] args) {
        A a = new A();
        a.method();
    }
}

内部类开放中的应用

package inside;

public interface I {
    void eat();
    void doing();
}
package inside;

public class OP {
    public void operation(I i) {
        i.eat();
        i.doing();
    }
}
package inside;

public class Demo {
    public static void main(String[] args) {
        OP op = new OP();

        op.operation(new I() {
            @Override
            public void eat() {
                System.out.println("猫吃鱼!");
            }
            
            @Override
            public void doing() {
                System.out.println("猫跳高!");
            }
        });

        op.operation(new I() {
            @Override
            public void eat() {
                System.out.println("狗吃骨头!");
            }

            @Override
            public void doing() {
                System.out.println("狗跳墙!");
            }
        });
    }
}

常用 API

Math

package math;

public class Demo {
    public static void main(String[] args) {
        System.out.println(Math.abs(-99)); // 绝对值
        System.out.println(Math.ceil(12.03) + "," + Math.ceil(12.93)); // 先上取整 返回double
        System.out.println(Math.floor(12.03) + "," + Math.floor(12.93)); // 先上取整 返回double
        System.out.println(Math.round(12.03) + "," + Math.round(12.93)); // 四舍五入 返回int
    }
}

System

package math;

public class Demo {
    public static void main(String[] args) {
        System.out.println("开始");
        System.exit(0); // 终止当前运行的虚拟机,非0表示异常终止
        System.out.println("结束");

        System.out.println(System.currentTimeMillis()); // 时间戳
    }
}

Object

Object是类层次结构的根。每个类都有Object作为超类。所有对象(包括数组)都实现了这个类的方法。

toString

System.out.println 调用了超类Object的toString()方法,不变阅读,

重写

public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
equals

Object类的equals方法,比较事会地址比较,

但两个对象中比较数据是否相同是,重写equals方法:

public boolean equals(Object o) {
        if (this == o) return true;
    // 判断两个类是否来自同一个类
        if (o == null || getClass() != o.getClass()) return false;

        Cat cat = (Cat) o;

        if (age != cat.age) return false; // 比较age是否相同
        return Objects.equals(name, cat.name); // 比较name是否相同
    }

Arrays

toSring & sort
import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
        int arr [] = { 1, 3, 9, 5, 6 };
        System.out.println(Arrays.toString(arr));
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

基本类型包装类

Integer
public class Demo {
    public static void main(String[] args) {
        System.out.println(Integer.MAX_VALUE); // 2147483647
        System.out.println(Integer.MIN_VALUE); // -2147483648
    }
}

等到Integer对象

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf(100);
        System.out.println(i1);
        Integer i2 = Integer.valueOf("100");
        System.out.println(i2);
    }
}

字符串分割

package math;

import java.util.Arrays;

public class Demo {
    public static void main(String[] args) {
        String s = "18 69 52 31 99 26 1 52";
        String arr [] = s.split(" ");
        int [] arrInt = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            arrInt[i] = Integer.parseInt(arr[i]);
        }
        Arrays.sort(arrInt);
        System.out.println(Arrays.toString(arrInt)); // [1, 18, 26, 31, 52, 52, 69, 99]
         StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < arrInt.length; i++) {
            if (i == arr.length - 1) {
                stringBuilder.append(arrInt[i]);
            } else {
                stringBuilder.append(arrInt[i]).append(" ");
            }
        }
        System.out.println(stringBuilder.toString()); // 1 18 26 31 52 52 69 99
    }
}

自动装箱和拆箱

  • 装箱:把基本数据类型转换为对应的包装类类型
  • 拆箱:把包装类类型转化为对应的基本数据类型
 Integer i = Integer.valueOf(100); // 装箱
 Integer ii = 100; // 自动装箱
 ii = ii.intValue() + 200; // 拆箱
 ii += 200; // 自动拆箱

Date

package math;

import java.util.Date;

public class Demo2 {
    public static void main(String[] args) {
        Date d = new Date();
        long time = d.getTime(); // 时间戳
        System.out.println(time);
        System.out.println(time * 1.0 / 1000 / 60 /60 / 24 / 365 + "年");
        Date d2 = new Date();
//        long time2 = 1000*60*60;
        long time2 = System.currentTimeMillis(); // 当前时间
        d2.setTime(time2);
        System.out.println(d2);
    }
}
SimpleDateFormat
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo2 {
    public static void main(String[] args) {
        Date d = new Date();
//        SimpleDateFormat s = new SimpleDateFormat();
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String str = s.format(d);
        System.out.println(str); // 2020-01-21 07:23:15
    }
}

Calendar

import java.util.Calendar;

public class Demo2 {
    public static void main(String[] args) {
        Calendar c = Calendar.getInstance();
        
     // c.add(Calendar.YEAR, 10); // 十年后
     // c.add(Calendar.DATE, -5); // 五天前
        
     // c.set(2099, 11, 12); // 设置时间,(月是从0开始的,要+1)
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH) + 1;
        int day = c.get(Calendar.DATE);
        System.out.println(year + "年" + month + "日" + day + "日"); // 2020年01月21日
    }
}

异常

  • Throwable
    • Error: 严重问题,不需要处理
    • Exception:异常类,表示程序本事可以处理
      • RuntimeException:再编译期是不检查的,出现问题后修改代码
      • 非 RuntimeException:编译期就必须处理,否则不能通过编译
try { } catch () { }
public class Demo {
    public static void main(String[] args) {
        System.out.println("--star--");
        try {
            method();
        } catch (ArrayIndexOutOfBoundsException error) {
            System.out.println("数组索引不存在," + error);
        }
        System.out.println("--end--");
    }
    public static void method() {
        int [] arr = { 1, 2, 3 };
        System.out.println(arr[3]);
    }
}
Throwable
public class Demo {
    public static void main(String[] args) {
        System.out.println("--star--");
        method();
        System.out.println("--end--");
    }
    public static void method() {
        try {
            int [] arr = { 1, 2, 3 };
            System.out.println(arr[3]);
        } catch (ArrayIndexOutOfBoundsException e) {
//            System.out.println(e.getMessage());
//            System.out.println(e.toString());
            e.printStackTrace();
        }
    }
}

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

运行时异常(非受检异常)
public class Demo {
    public static void main(String[] args) {
        method();
    }
    public static void method() {
        int [] arr = { 1, 2, 3 };
        System.out.println(arr[3]);
    }
}
编译时异常(受检异常)
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {
    public static void main(String[] args) {
        method();
    }
    public static void method() {
       try {
           String s = "2050-08-08";
           SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
           Date d = sdf.parse(s);
           System.out.println(d);
       } catch (ParseException e) {
           e.printStackTrace();
       }
    }
}

throws 处理异常

public class Demo {
    public static void main(String[] args) {
        method();
    }
    public static void method() throws ArrayIndexOutOfBoundsException { // throws
        int [] arr = { 1, 2, 3 };
        System.out.println(arr[3]);
    }
}

throws 只是抛出异常,并没有对异常实际处理

自定义异常

package unusual;

public class ScoreException extends Exception {
    public ScoreException () {}
    public ScoreException (String msg) {
        super(msg);
    }
}
package unusual;

public class Teacher {
    public void checkScore (int score) throws ScoreException {
        if (score < 0 || score > 100) {
            throw new ScoreException("分数应该在0到100之间");
        } else {
            System.out.println("分数正常");
        }
    }
}
package unusual;

public class Demo {
    public static void main(String[] args) {
        Teacher T = new Teacher();
        try {
            T.checkScore(10);
            T.checkScore(1000);
        } catch (ScoreException e) {
            e.printStackTrace();
        }
    }
}

集合

集合类型体系

  • 集合
    • Collection 单列(接口)
      • List 可重复(接口)
        • ArrayList(实现类)
        • linkedlist(实现类)
        • ······
      • Set 不可重复(接口)
        • HashSet(实现类)
        • TreeSet(实现类)
        • ······
    • Map 双列(接口)
      • HashMap(实现类)
      • ······

Collection

import java.util.ArrayList;
import java.util.Collection;

public class CollectionDemo {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>(); // 多态形式创建对象
        System.out.println(c.add("hello!")); // add返回到收拾 true
        c.add("girl"); // 添加
        System.out.println(c); // [hello!, girl]
        System.out.println(c.size()); // 2  长度
        System.out.println(c.contains("girl")); // true 判断集合中指定的元素
        System.out.println(c.isEmpty()); // false 判断是否为空
        System.out.println(c.remove("hello!")); // true 删除指定元素
        System.out.println(c); // [girl]
        c.clear(); // 清空
        System.out.println(c); // []
    }
}
Collection 遍历

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

  • IteratorIterator(): 返回此集合中元素的迭代器,痛过集合的 Iterator() 方法得等到
  • 迭代器是通过集合的Iterator() 方法得到的,所以我们说它是依赖于集合而存在的
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class CollectionDemo2 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<String>(); // 多态形式创建对象
        c.add("hello!");
        c.add("girl,");
        c.add("good");
        Iterator<String> it = c.iterator();
        /**
        // next() 返回迭代器中的下一个元素
        if (it.hasNext())  // 判断是否有元素
            System.out.println(it.next()); // hello!
        if (it.hasNext())
            System.out.println(it.next()); // girl,
        if (it.hasNext())
            System.out.println(it.next()); // good
        if (it.hasNext())
            System.out.println(it.next()); // 不执行
        */
        // 遍历
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

List

  • 有序
  • 可重复
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ListDEmo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("girl");
        list.add("good");
        list.add("hello"); // 可重复
        System.out.println(list); // [hello, girl, good, hello]
        list.add(3,"beautiful");
        System.out.println(list); // [hello, girl, good, beautiful, hello]
        list.remove(4);
        System.out.println(list); // [hello, girl, good, beautiful]
        list.set(0, "你好!");
        System.out.println(list); // [你好!, girl, good, beautiful]
        System.out.println(list.get(2)); // good
        // 迭代器遍历
        /**
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }*/
        for (int i = 0; i < list.size(); i ++) {
            System.out.println(list.get(i));
        }
    }
}

并发修改异常

并发异常

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ConcurrentModificationException {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String s = it.next();
            if(s.equals("world")) {
                list.add("javaee");
            }
        }
        System.out.println(list);
    }
}
/** 源码分析:
	实际修改的次数 != 预期修改的次数
	会抛出异常
	add() 对 实际修改的次数 ++
*/

解决

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ConcurrentModificationException {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");
        Iterator<String> it = list.iterator();
        for (int i = 0; i < list.size(); i++) {
            String s = list.get(i);
            if(s.equals("world")) {
                list.add("javaee");
            }
        }
        System.out.println(list);
    }
}

列表迭代器

package collection;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

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> listIt = list.listIterator();
        /**
        // 正向遍历
        while (listIt.hasNext()) {
            String s = listIt.next();
            System.out.println(s);
        }
        // 逆向遍历
        while (listIt.hasPrevious()) {
            String s = listIt.previous();
            System.out.println(s);
        }*/
        while (listIt.hasNext()) {
            String s = listIt.next();
            if (s.equals("hello")) {
                listIt.add("javaee");
            }
        }
        System.out.println(list); // [hello, javaee, world, java]
    }
}

增强 for

int [] arr = { 1, 2, 3, 4 };
for (int i : arr) {
    System.out.println(i);
}
List<String> list = new ArrayList<String>();
list.add("hello");
list.add("world");
list.add("java");
for (String s : list) {
    System.out.println(s);
}
// 存在并发修改异常

链表 LinkedList

ArrayList底层数据结构:数组查询快,整删慢
LinkedList底层数据结构:链表查询忙,增删快
public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> linkList = new LinkedList<>();
        linkList.add("hello");
        linkList.add("javaee");
        linkList.add("奥利给");
        for (String s : linkList) {
            System.out.println(s);
        }
        
        for (int i = 0; i < linkList.size(); i ++) {
            System.out.println(linkList.get(i));
        }

        ListIterator<String> it = linkList.listIterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

LinkedList特有方法

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> linkList = new LinkedList<>();
        linkList.add("hello");
        linkList.add("javaee");
        linkList.add("奥利给");

        linkList.addFirst("star"); // 1.开头插入元素
        linkList.addLast("end"); // 2.末尾追加元素
        System.out.println(linkList); // [star, hello, javaee, 奥利给, end]
        System.out.println(linkList.getFirst()); // star 3.获取开头元素
        System.out.println(linkList.getLast()); // end 4.获取末尾元素
        System.out.println(linkList.removeFirst()); // star 5.删除并返回第一个元素
        System.out.println(linkList.removeLast()); // end 6.删除并返回最后一个元素
        System.out.println(linkList); // [hello, javaee, 奥利给]
    }
}

Set 集合

不能包含重复元素

hashSet:对集合的迭代顺序不做任何保证

import java.util.HashSet;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();
        set.add("hello");
        set.add("hello");
        set.add("java");
        set.add("奥利给");
        System.out.println(set); // [java, hello, 奥利给]
        for (String s : set) {
            System.out.println(s); // java hello 奥利给  无序
        }
    }
}

哈希值

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

Object类中 hashCode() 方法可以获取对象的哈希值

public class HashCodeDemo {
    public static void main(String[] args) {
        Student s1 = new Student("牛牛", 18, 1);
        Student s2 = new Student("牛牛", 18, 1);
        /**
         * 默认情况下:不同对象的哈希只是不同的
         * 通过方法重写,不同对象的哈希值可以相同
         * */
        System.out.println(s1.hashCode()); // 1844169442
        System.out.println(s2.hashCode()); // 1537358694
        // 字符串重写了hashCode()
        System.out.println("重地".hashCode()); // 1179395
        System.out.println("通话".hashCode()); // 1179395
    }
}

HashSet

import java.util.HashSet;

public class SetDemo {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<String>();
        set.add("hello");
        set.add("hello");
        set.add("java");
        set.add("奥利给");
        System.out.println(set); // [java, hello, 奥利给]
        for (String s : set) {
            System.out.println(s); // java hello 奥利给  无序
        }
    }
}

哈希表

JDK8之前,底层采用 数组 + 链表 实现,可以说是一个元素为列表数组

JDK8 以后, 在长度比较长的时候,底层实现了优化

默认长度为16 >> hashCode % 16 获得存储位置 >> 如果相同位置有元素 >> 比较hashCode >> 如果hashCode有相同 >> 比较内容

LinkedHashSet

有序,具有可预测的迭代次序

不可重复

import java.util.LinkedHashSet;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        LinkedHashSet<String> set = new LinkedHashSet<>();
        set.add("hello");
        set.add("hello");
        set.add("java");
        set.add("奥利给");
        System.out.println(set); // [java, hello, 奥利给]
        for (String s : set) {
            System.out.println(s); // java, hello, 奥利给  有序
        }
    }
}

TreeSet

  • 元素有序(这里的顺序指的不是存储到取出的顺序,而是按照一定顺序排序,排序方法取决于构造方法)

    • TreeSet(): 根据其元素的自然排查进行排序
    • TreeSet(Comparator comparator): 根据比较器进行比较
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Integer> set = new TreeSet<>();
        set.add(99);
        set.add(52);
        set.add(160);
        set.add(52);
        System.out.println(set); // [52, 99, 160] 不可重复
        for (Integer s : set) {
            System.out.println(s); // 52, 99, 160
        }
    }
}

自定义排序

package collection;

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 int getAge() { return age; }

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

    public void setAge(int age) { this.age = age; }
    
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    /**
     * 重写 compareTo 方法,自定义排序规则
     * */
    @Override
    public int compareTo(Student s) {
        // int num = this.age - s.age; // 升序
        // int num = s.age - this.age; // 降序
        /**
         * 去重,年龄相同 && 姓名相同
         * */
        int num = s.age - this.age;
        num = num == 0 ? this.name.compareTo(s.name) : num;
        return num;
    }
}
public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> set = new TreeSet<Student>();
        set.add(new Student("乔丹", 60));
        set.add(new Student("詹姆斯", 35));
        set.add(new Student("詹姆斯", 35));
        set.add(new Student("娜娜", 18));

        System.out.println(set); 
        for (Student s : set) {
            System.out.println(s);
        }
    }
}

比较器排序 Comparator 的使用

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet<Student> set = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // int num = s1.getAge() - s2.getAge(); // 正序
                // int num = s2.getAge() - s1.getAge(); // 倒序
                /**
                 * 倒序 去重
                 */
                int num = s2.getAge() - s1.getAge();
                num = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num;
            }
        });
        set.add(new Student("乔丹", 60));
        set.add(new Student("詹姆斯", 35));
        set.add(new Student("詹姆斯", 35));
        set.add(new Student("娜娜", 18));

        System.out.println(set); // [Student{name='乔丹', age=60}, Student{name='詹姆斯', age=35}, Student{name='娜娜', age=18}]
        for (Student s : set) {
            System.out.println(s); // 52, 99, 160
        }
    }
}
  • 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序
  • 比较器排序。就是让集合构造方法接收Comparator的实现类对象,重写compare(T t1, T t2)

泛型

泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型**,也就是说所操作的数据类型被指定为一个参数提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型这种参数类型可以用在方法接口中,分别被称为泛型类、泛型方法、泛型接口

  • 把运行时期的问题提前到了编译期间
  • 避免了强制类型转换
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList();
        c.add("aa");
        c.add("bb");
        c.add("cc");
//        c.add(100);
        Iterator<String> it = c.iterator();
        while (it.hasNext()) {
//             Object obj = it.next();
            String obj = (String) it.next();
            System.out.println(obj);
        }
    }
}

泛型类 & 泛型方法

public class Genericity<T> {
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}
public class Demo {
    public static void main(String[] args) {
        Genericity<String> g1 = new Genericity<String>();
        g1.setT("阿卜杜");
        System.out.println(g1.getT()); // 阿卜杜
        Genericity<Integer> g2 = new Genericity<Integer>();
        g2.setT(19); // 19
        System.out.println(g2.getT());
    }
}

泛型接口

public interface GenericityInterface<T> {
    void show (T t);
}
public class GenericityInterfaceImpl<T> implements GenericityInterface<T> {
    public void show(T t) {
        System.out.println(t);
    }
}
public class Demo {
    public static void main(String[] args) {
        GenericityInterfaceImpl<String> g1 = new GenericityInterfaceImpl<String>();
        g1.show("阿卜杜"); // 阿卜杜
        GenericityInterfaceImpl<Integer> g2 = new GenericityInterfaceImpl<Integer>();
        g2.show(19); // 19
    }
}

类型通配符

为了表示各种泛型List的父类,可以使用类型通配符

  • 类型通配符: <?>

  • List<?>: 表示元素类型未知的List,它的元素可以匹配任何的类型

  • 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中

如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限

  • 类型通配符上限: <?extends 类型>
  • List<?extends Number>: 它表示的类型是Number或者其子类型

除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限

public class Demo {
    public static void main(String[] args) {
        // 通配符
        List<?> list1 = new ArrayList<Number>();
        List<?> list2 = new ArrayList<String>();
        // 类型通配符上限
        List<? extends Number> list3 = new ArrayList<Number>();
        List<? extends Number> list4 = new ArrayList<Integer>();
        // 类型通配符的下限
        List<? super Number> list5 = new ArrayList<Object>();
        List<? super String> list6 = new ArrayList<Object>();
    }
}

可变参数

import java.util.Arrays;
import java.util.List;
import java.util.Set;

public class Demo {
    public static void main(String[] args) {
        /**
         * 固定 不可变,不能增、删操作
         * */
        List<String> aList1 = Arrays.asList("aa", "bb", "cc"); // 不可变
        System.out.println(aList1); // [aa, bb, cc]
        List<String> aList2 = List.of("aa", "bb", "cc"); // 不可变
        System.out.println(aList2); // [aa, bb, cc]
        Set<String> aList3 = Set.of("aa", "bb", "cc"); // 不可变 不可重复
        System.out.println(aList3); // [aa, bb, cc]
    }

Map集合

  • Interface Map 键的类型;V:值的类型
  • 将键映射到值的对象,不能包含重复的键,每个键可以映射到最多一个值

创建Map集合的对象

  • 多态的方式
  • 具体的实现类HashMap

Map集合操作

import java.util.HashMap;
import java.util.Map;

public class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        // 添加元素
        map.put("s001", "阿卜杜");
        map.put("s002", "詹姆斯");
        map.put("s002", "刚刚"); // 重复key, 会被覆盖
        System.out.println(map); // {s002=刚刚, s001=阿卜杜}
        // 移除
        map.remove("s002");
        System.out.println(map); // {s001=阿卜杜}
        // 判断是否包含指定的key
        System.out.println(map.containsKey("s002")); // false
        // 判断是否包含指定的value
        System.out.println(map.containsValue("阿卜杜")); // true
        // 是否为空
        System.out.println(map.isEmpty()); // false
        // get 长度
        System.out.println(map.size()); // 1
        // 清空所有
        map.clear();
        System.out.println(map); // {}
    }
}

Map集合获取功能

import java.util.HashMap;
import java.util.Map;

public class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("s001", "阿卜杜");
        map.put("s002", "詹姆斯");
        // 根据 key 获取 value
        System.out.println(map.get("s001")); // 阿卜杜
        // 获取所有 key 的集合
        System.out.println(map.keySet()); // [s002, s001]
        // 获取所有 value 的集合
        System.out.println(map.values()); // [詹姆斯, 阿卜杜]
        // 获取所有 key value 对象的集合
        System.out.println(map.entrySet()); // [s002=詹姆斯, s001=阿卜杜]
    }
}

Map 遍历

import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("s001", "阿卜杜");
        map.put("s002", "詹姆斯");
        map.put("s003", "古德龙");
		// 方式1
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            System.out.println(key + ":" + map.get(key));
        }
		// 方式2
        Set<Map.Entry<String, String>> entry = map.entrySet();
        for (Map.Entry<String, String> en : entry) {
            System.out.print(en.getKey() + "-");
            System.out.println(en.getValue());
        }
    }
}

存储集合时,重写 equals(Object o) 和 hashCode(),实现去重

@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;
        if (!Objects.equals(sId, student.sId)) return false;
        return Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        int result = sId != null ? sId.hashCode() : 0;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        return result;
    }

打乱集合元素(洗牌)

Collections.shuffle(arrayList)

I / O流

File类概述和构造方法

File:它是文件和目录路径名的抽象表示

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

构造方法


import java.io.File;

public class Demo {
    public static void main(String[] args) {
        /**
         *  File(String pathname)
         *  通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
         *  只是抽象路径的表示形式,不需要一定要存在该文件
         */
        File file1 = new File("D:\\file_test\\a.txt");
        /**
         * File(String parent, String child)
         * 从父路径名字符串和子路径名字符串创建新的 File实例
         */
        File file2 = new File("D:\\file_test","a.txt");
        /**
         * File(File parent,String child)
         * 从父抽象路径名和子路径名字符串创建新的File实例
         */
        File f = new File("D:\\file_test");
        File file3 = new File(f,"a.txt");

        System.out.println(file1); // D:\file_test\a.txt
        System.out.println(file2); // D:\file_test\a.txt
        System.out.println(file3); // D:\file_test\a.txt
    }
}

创建功能

import java.io.File;
import java.io.IOException;

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

        /**
         * public boolean createNewFile()
         * 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空**文件**
         */
        File file1 = new File("D:\\file_test\\a.txt");
        System.out.println(file1.createNewFile()); // 不存在,创建文件,返回 true;存在返回false
        /**
         * public boolean mkdir()
         * 创建由此抽象路径名命名的**目录**
         */
        File file2 = new File("D:\\file_test", "\\java");
        System.out.println(file2.mkdir());
        /**
         * public boolean mkdirs()
         * 创建由此抽象路径名命名的目录,包括任何必需但不存在的父**目录**
         */
        File file3 = new File(file2, "\\Spring");
        System.out.println(file3.mkdirs()); 
    }
}

获取功能

import java.io.File;
import java.io.IOException;

public class GetFileDemo {
    public static void main(String[] args) throws IOException {
        File file1 = new File("D:\\file_test\\a.txt");
        File file2 = new File("D:\\file_test");
        /**
         * public boolean isDirectory()
         * 测试此抽象路径名表示的File是否为目录
         * */
        System.out.println(file1.isDirectory()); // false
        System.out.println(file2.isDirectory()); // true
        /**
         * public boolean isFile()
         * 测试此抽象路径名表示的File是否为文件
         * */
        System.out.println(file1.isFile()); // true
        System.out.println(file2.isFile()); // false
        /**
         * public boolean exists()
         * 测试此抽象路径名表示的File是否存在
         */
        System.out.println(file1.exists()); // true
        System.out.println(file2.exists()); // true
        /**
         * public String getAbsolutePath()
         * 返回此抽象路径名的绝对路径名字符串
         */
        System.out.println(file1.getAbsolutePath()); // D:\file_test\a.txt
        System.out.println(file2.getAbsolutePath()); // D:\file_test
        /**
         * public String getPath()
         * 将此抽象路径名转换为路径名字符串
         */
        System.out.println(file1.getPath()); // D:\file_test\a.txt
        System.out.println(file2.getPath()); // D:\file_test
        /**
         * public String getName()
         * 返回由此抽象路径名表示的文件或目录的名称
         */
        System.out.println(file1.getName()); // a.txt
        System.out.println(file2.getName()); // file_test
        /**
         * public String[] list()
         * 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
         */
        showArray(file1.list()); // null
        showArray(file2.list()); // [ a.txt, java ]
        /**
         * public File[] listFiles()
         * 返回此抽象路径名表示的目录中的文件和目录的File对象数组
         */
        showArray(file1.listFiles()); // null
        showArray(file2.listFiles()); // [ D:\file_test\a.txt, D:\file_test\java ]
    }
    public static void showArray(File[] arrays) {
        if(arrays == null || arrays.length == 0) System.out.println("null");
        else for (File a : arrays) System.out.println(a);
    }
    public static void showArray(String[] arrays) {
        if(arrays == null || arrays.length == 0) System.out.println("null");
        else for (String a : arrays) System.out.println(a);
    }
}

删除功能

import java.io.File;
import java.io.IOException;

public class DelFileDemo {
    public static void main(String[] args) throws IOException {
        File file1 = new File("D:\\file_test\\a.txt");
        File file2 = new File("D:\\file_test\\java");
        /**
         * 删除由此抽象名表示对文件或目录
         */
        System.out.println(file1.delete());
        System.out.println(file2.delete()); // 目录下有内容不能直接删除
    }
}

递归 获取所有文件的绝对路径

import java.io.File;
import java.io.IOException;

public class RecursiveDirectory {
    public static void main(String[] args) throws IOException {
        File topLevelDirectory = new File("D:\\file_test");
        method(topLevelDirectory);
    }
    static void method(File f) {
        if (f == null) return;
        File[] files = f.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                method(file);
            } else {
                System.out.println(file.getAbsolutePath());
            }
        }
    }
}

lO 流概述和分类

  • IO:输入/输出(Input/Output)

  • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输

  • lO流就是用来处理设备间数据传输问题的

    ​ 常见的应用:文件复制;文件上传;文件下载

  • lO的分类

    • 输入流:读数据
    • 输出流:写数据

字节流写数据

  • InputStream:这个抽象类是表示字节输入流的所有类的超类;
  • OutputStream:这个抽象类是表示字节输出流的所有类的超类;
  • 子类名特点:子类名称都是以其父类名作子类名的后缀;

写 FileOutputStream

package file;

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

public class OutputStreamDemo {
    public static void main(String[] args) throws IOException {
        /**
         * FileOutputstream(File file)
         * 创建文件输出流以写入由指定的 eile对象表示的文件。
         */
        FileOutputStream fileOutput = new FileOutputStream(new File("D:\\file_test\\a.txt"));
        byte [] arr = "Hello 你好啊".getBytes();
        // fileOutput.write(97);
        fileOutput.write(arr,0, arr.length);

        fileOutput.close();
        /**
         * FileOutputStream(FileDescriptor fdobj)
         * 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
         */


        /**
         * FileOutputStream(File file, boolean append)
         * 创建文件输出流以写入由指定的 eile对象表示的文件。
         * append: true 追加写入
         */


        /**
         * FileOutputstream(string name)
         * 创建文件输出流以指定的名称写入文件
         */
        FileOutputStream fileOutput2 = new FileOutputStream("D:\\file_test\\b.txt");
        for (int i = 0; i < 10; i++) {
            fileOutput2.write("hello".getBytes());
            fileOutput2.write("\r\n".getBytes()); // 换行
        }
        fileOutput2.close();
        /**
         * FileOutputstream(string name, boolean append)
         * 创建文件输出流以指定的名称写入文件。
         */
        FileOutputStream fileOutput3 = new FileOutputStream("D:\\file_test\\c.txt", true);
        fileOutput3.write("hello FileOutputStream;".getBytes());
        fileOutput3.write("\r\n".getBytes()); // 换行
        fileOutput3.close();
    }
}

字节流写数据加异常处理

finally: 在异常处理时提供finally块来执行所有清除操作。比如说lO流中的释放资源

import java.io.FileOutputStream;
import java.io.IOException;

public class FileTryCatchDemo {
    public static void main(String[] args) {
        FileOutputStream fileOutput = null;
        try {
            fileOutput = new FileOutputStream("D:\\file_test\\a.txt");
            byte [] arr = "Hello 你好啊".getBytes();
            fileOutput.write(arr,0, arr.length);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileOutput != null) {
                try {
                    fileOutput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

读 FileInputStream

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\file_test\\a.txt");
        /**
        for (;;) {
            int b = fis.read();
            if (b == -1) {
                break;
            }
            System.out.print((char) b);
        }*/
        int by;
        while ((by = fis.read())!=-1) {
            System.out.print((char) by);
        }
        fis.close();
    }
}

复制

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

public class FileCopeDemo {
    public static void main(String[] args) throws IOException {
         // D:\file_test\a.txt 复制到 D:\file_test\java\a.txt
        FileInputStream input = new FileInputStream("D:\\file_test\\a.txt");
        FileOutputStream output = new FileOutputStream("D:\\file_test\\java\\a.txt", true);
        int by;
        while ((by = input.read())!=-1) {
            output.write(by);
        }
        input.close();
        output.close();
    }
}
每次复制一个字节数字
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopeDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream input = new FileInputStream("D:\\file_test\\a.txt");
        FileOutputStream output = new FileOutputStream("D:\\file_test\\java\\a.txt", true);
        byte [] bys = new byte[1024];
        int len;
        while ((len = input.read(bys)) != -1) {
            output.write(bys);
        }
        input.close();
        output.close();
    }
}

复制图片

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

public class CopyImgDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream input = new FileInputStream("D:\\file_test\\m.jpg");
        FileOutputStream output = new FileOutputStream("D:\\file_test\\java\\m.jpg", true);
        byte [] bys = new byte[1024];
        int len;
        while ((len = input.read(bys)) != -1) {
            output.write(bys);
        }
        input.close();
        output.close();
    }
}

字节缓冲流

BufferedOutputStream

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

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
  • BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 |

BufferedInputStream

BufferedInputStream为另一个输入流添加了功能,即缓冲输入和支持markreset方法的功能。 当创建BufferedInputStream时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。 mark操作会记住输入流中的一点,并且reset操作会导致从最近的mark操作之后读取的所有字节在从包含的输入流中取出新的字节之前重新读取。

  • BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
  • BufferedInputStream(InputStream in, int size) 创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
import java.io.*;

public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        // 缓冲流复制视频
        long startTime = System.currentTimeMillis();
        BufferedInputStream  buff_input = new BufferedInputStream(new FileInputStream("D:\\file_test\\test.mp4"));
        BufferedOutputStream buff_output = new BufferedOutputStream(new FileOutputStream("D:\\file_test\\java\\test.mp4", true));
        byte [] bys = new byte[1024];
        int len;
        while ((len = buff_input.read(bys)) != -1) {
            buff_output.write(bys);
        }
        buff_input.close();
        buff_output.close();
        long endTime = System.currentTimeMillis();
        System.out.println("共耗時" + (endTime - startTime) + "ms");
    }
}

字符流

  • 由于字节流操作中文不是特别的方便,所以ava就提供字符流
    • 字符流 = 字节流 + 编码表
  • 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
    • 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

编码表

  • 计算机中储存的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
  • 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。(必须按照同种编码解析,才能显示正确的文本符号)
ASCI字符集
  • ASCII(American StandardCode for lnformation lnterchange,美国信息交换标准代码): 是基于拉]字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
  • 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
GBXXX字符集:
  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码就是常说的”全角"字符,而原来在127号以下的那些就叫“半角"字符了
  • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
  • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等
Unicode字符集:
  • 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码
  • UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用至四个字节为每个字符编码
    编码规则:
    128个US-ASCII字符,只需一个字节编码
    拉丁文等字符,需要二个字节编码
    大部分常用字(含中文),使用三个字节编码
    其他极少使用的Unicode辅助字符,使用四字节编码

字符串的编码解码

编码
  • byte[] getBytes():使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
  • byte[] getBytes(String charsetName): 使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码
  • String(byte[] bytes): 通过使用平台的默认字符集解码指定的字节数组来构造新的String
  • String(byte[] bytes, String charsetName): 通过指定的字符集解码指定的字节数组来构造新的String
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class SringDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "地球";
        byte [] bys1 = s.getBytes();
        System.out.println(Arrays.toString(bys1)); // [-27, -100, -80, -25, -112, -125]
        byte [] bys2 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bys2)); // [-75, -40, -57, -14]
        String s1 = new String(bys1);
        System.out.println(s1); // 地球
        String s2 = new String(bys2, "GBK");
        System.out.println(s2); // 地球
    }
}

字符流的基类

字符流的抽象类

  • Reader:字符输入流的抽象类
  • Writer:字符输出流的抽象类

字符流中和编码解码问题相关的两个类

  • InputStreamReader
  • OutputStreamWriter
import java.io.*;

public class ReaderWriterDemo {
    public static void main(String[] args) throws IOException {
        // 写
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\file_test\\a.txt"));
        osw.write("中华人民共和国");
        osw.close();

        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("D:\\file_test\\b.txt"), "GBK");
        osw2.write("中华人民共和国");
        osw2.close();
        // 读
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\file_test\\a.txt"));
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.print((char)ch);
        }
        isr.close();

        InputStreamReader isr2 = new InputStreamReader(new FileInputStream("D:\\file_test\\b.txt"), "GBK");
        int ch2;
        while ((ch2 = isr2.read()) != -1) {
            System.out.print((char)ch2);
        }
        isr2.close();
    }
}
复制
import java.io.*;

public class CopyReaderWriterDemo {
    public static void main(String[] args) throws IOException {
        long sTime = System.*currentTimeMillis*();
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\file_test\\晴天.txt"));
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:\\file_test\\java\\晴天.txt"));
        char [] chars = new char[1024];
        int len;
        while ((len = isr.read(chars)) != -1) {
            osw.write(chars);
        }
        osw.flush(); // 刷新
        isr.close();
        osw.close();
        long eTime = System.*currentTimeMillis*();
        System.*out*.println("耗时:" + (eTime - sTime)+ "ms");
    }
}

字符缓冲流

import java.io.*;

public class BufferedWriter_ReaderDemo {
    public static void main(String[] args) throws IOException {
        // 复制
        long sTime = System.currentTimeMillis();
        BufferedReader br = new BufferedReader(new FileReader("D:\\file_test\\晴天.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\file_test\\java\\晴天.txt"));
        /**
         * 方法 一:
        char [] chars = new char[1024];
        int len;
        while ((len = br.read(chars)) != -1) {
            bw.write(chars);
        }*/
        // 方法二:
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
        }
        bw.flush(); // 刷新
        br.close();
        bw.close();
        long eTime = System.currentTimeMillis();
        System.out.println("耗时:" + (eTime - sTime)+ "ms");
    }
}

集合 to 文件

import java.io.*;

public class ArrayListToFileDemo {
    public static void main(String[] args) throws IOException {
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("yngxtt001", "李白", 1000));
        list.add(new Student("yngxtt002", "苏东坡", 800));
        list.add(new Student("yngxtt003", "杜甫", 900));
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\file_test\\诗人.txt"));
        for (Student s : list) {
            StringBuilder str = new StringBuilder();
            str.append(s.getId()).append(",").append(s.getName()).append(",").append(s.getAge());
            bw.write(str.toString());
            bw.newLine();
        }
        bw.flush();
        bw.close();
    }
}

文件 to 集合

import java.io.*;
import java.util.ArrayList;

public class FileToArrayListDemo {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("D:\\file_test\\诗人.txt"));
        ArrayList<Student> list = new ArrayList<>();
        String line;
        while ((line = br.readLine()) != null) {
             String [] sArr = line.split(",");
             Student s = new Student(sArr[0], sArr[1], Integer.parseInt(sArr[2]));
             list.add(s);
        }

        br.close();
        for (Student s : list) {
            System.out.println(s);

        }
    }
}

复制单级文件夹

package file;

import java.io.*;

public class CopyFirstLevelFolderDemo {
    public  static void main(String[] args) throws IOException {
        File srcFolder = new File("D:\\test_file");
        String srcFolderName = srcFolder.getName(); // 获取File对象的名称
        File destFolder = new File("D:\\test_file_copy");
        if (!destFolder.exists()) { // 判断是否存在
            destFolder.mkdir();
        }
        File[] listFiles = srcFolder.listFiles(); // 获取所有文件的File数组
        for (File srcFile : listFiles) {
            String srcFileName = srcFile.getName();
            File destFile = new File(destFolder, srcFileName);
            copyFile(srcFile, destFile);
        }
    }
    private static void copyFile(File srcFile, File destFile) throws IOException {
        System.out.println(srcFile);
        System.out.println(destFile);

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        byte [] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        bis.close();
        bos.close();
    }
}

复制多级文件夹

标准输入流

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 键盘输入
String line = br.readLine();
System.out.println(line);

标准输出流

PrintStream ps = System.out;
ps.println("test");

字节打印流

public class Demo {
    public  static void main(String[] args) throws IOException {
        PrintStream ps = new PrintStream("D:\\test_file\\hello.txt");
        // ps.write(97);
        ps.print(97);
        ps.println(99);
        ps.print(100);
        ps.close();
    }
}

字符打印流

public class Demo {
    public  static void main(String[] args) throws IOException {
        PrintWriter pw = new PrintWriter(new FileWriter("D:\\test_file\\hello.txt"));
        pw.print("hello");
        pw.println("你好");
        pw.print("再见!");
        pw.close();
    }
}

序列化流

对象序列化流

对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示个对象, 该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了-一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

public class ObjectOutputStreamDemo {
    public  static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test_file\\oos.txt"));
        Student s = new Student("大卫", 18);
        oos.writeObject(s);
        oos.close();
    }
}
import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    
    public Student (String name, int age) {
        this.name = name;
        this.age = age;
    }
}

对象反序列化流

public class ObjectInputStreamDemo {
    public  static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test_file\\oos.txt"));
        Object obj = ois.readObject();
        Student s = (Student)obj;
        System.out.println(s.getName() + "," + s.getAge()); // 大卫,18
    }
}

当对象类的版本不一致时(有修改)

需声明可序列号ID值

public class Student implements Serializable {
    static final long serialVersionUID = 42L;
}

Stream 流

link

Properties

  • 是一个map体系的集合
  • Properties可以保存到流中过从流中加载
public class PropertiesDemo {
    public  static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        /*
        prop.put("s001", "大卫");
        prop.put("s002", "中卫");
        prop.put("s003", "小卫");
        Set<Object> keySet = prop.keySet();
        for (Object key : keySet) {
            System.out.println(key + "," + prop.get(key));
        }*/
        prop.setProperty("s001", "大卫");
        prop.setProperty("s002", "中卫");
        prop.setProperty("s003", "小卫");
        Set<String> keySet = prop.stringPropertyNames();
        for (String key : keySet) {
            System.out.println(key + "," + prop.getProperty(key));
        }
    }
}

数据<=>集合

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesToDemo {
    public  static void main(String[] args) throws IOException {
        // 集合 -> 数据
        store();
        // 数据 -> 集合
        loadr();
    }
    private static void store() throws IOException {
        Properties prop = new Properties();
        prop.setProperty("s001", "大卫");
        prop.setProperty("s002", "中卫");
        prop.setProperty("s003", "小卫");
        FileWriter fw = new FileWriter("D:\\test_file\\fw.txt");
        prop.store(fw, null);
        fw.close();
    }
    private static void loadr() throws IOException {
        Properties prop = new Properties();
        FileReader fr = new FileReader("D:\\test_file\\fw.txt");
        prop.load(fr);
        fr.close();
        System.out.println(prop); // {s003=小卫, s002=中卫, s001=大卫}
    }
}

多线程

进程:是正在运行的程序

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

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

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

多线程的实现方式- 方式1

继承 Thread 类,重写 run() 方法,启动方法

run():封装线程执行的代码,需要start() 调用才能启动多线程

public class ThreadClass extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }
}
public class ThreadDemo {
    public  static void main(String[] args) {
        ThreadClass tc1 = new ThreadClass();
        ThreadClass tc2 = new ThreadClass();
        // start()启动线程,java虚拟机调用此线程的 run()
        tc1.start();
        tc2.start();
    }
}

设置 和 获取 线程名称

public class ThreadClass extends Thread {
    ThreadClass() {}
    ThreadClass(String name) {
        super(name);
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(getName()+","+i);
        }
    }
}
public class ThreadDemo {
    public  static void main(String[] args) {
        ThreadClass tc1 = new ThreadClass();
        ThreadClass tc2 = new ThreadClass("第二个测试线程");
        // start()启动线程,java虚拟机调用此线程的 run()
        tc1.setName("第一个测试线程");
        tc1.start();
        tc2.start();
        System.out.println(tc1.getName() + tc2.getName());
    }
}

线程调度

线程有两种调度模型

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

假如计算机只有一一个CPU,那么CPU在某一个时刻只能执行一条指令, 线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的

线程优先级

public class PriorityDemo {
    public  static void main(String[] args) {
        ThreadClass tc1 = new ThreadClass("one");
        ThreadClass tc2 = new ThreadClass("tow");
        ThreadClass tc3 = new ThreadClass("there");
        // 获取线程优先级
        System.out.println(tc1.getPriority()); // 5
        System.out.println(tc2.getPriority()); // 5
        System.out.println(tc3.getPriority()); // 5
         // 设置线程优先级
        tc1.setPriority(10); // nin: 1, max: 10, norm:5(默认)
        tc2.setPriority(5);
        tc3.setPriority(1);
       
        tc1.start();
        tc2.start();
        tc3.start();
    }
}

线程控制

  1. 等待
public class ThreadClass extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.print(getName()+","+i);
            /**
             * sleep() 线程等待 指定时间 ms
             * */
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 等待这个线程死亡

package thread;

public class ControlDemo {
    public  static void main(String[] args) throws InterruptedException {
        ThreadClass tc1 = new ThreadClass("one");
        ThreadClass tc2 = new ThreadClass("tow");
        /**
         * join() 等待这个线程死亡
         */
        tc1.join();
        tc2.start();
    }
}
  1. 守护线程
public class ControlDemo {
    public  static void main(String[] args) throws InterruptedException {
        ThreadClass tc1 = new ThreadClass("one");
        ThreadClass tc2 = new ThreadClass("tow");
        // there 设为主线程
        Thread.currentThread().setName("there");
        /**
         * 将此线程标记为守护现场,当运行的现场都是守护线程时,java虚拟机将退出
         * 主线程死亡,守护线程也将杀死
         */
        tc1.setDaemon(true);
        tc2.setDaemon(true);
        tc1.start();
        tc2.start();

        // there
        for (int i = 0; i < 9; i ++) {
            System.out.println(Thread.currentThread().getName() + ':' + i);
        }
    }
}

线程生命周期

在这里插入图片描述

多线程的实现方式- 方式2

实现 Runnable 接口

package thread;

public class RunnableClass implements  Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i ++) {
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
public class RunnableDemo {
    public  static void main(String[] args) {
        RunnableClass rc = new RunnableClass();
        Thread t1 = new Thread(rc);
        Thread t2 = new Thread(rc);
        t1.start();
        t2.start();
    }
}

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

  • 避免了 Java 单继承的局限性

线程同步

案例:卖票

public class SellTicket implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        for(;;) {
            if (tickets > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":卖了第"+ (100 - tickets) + "票");
                tickets --;
            }
        }
    }
}
public class SellTicketDemo {
    public  static void main(String[] args) {
        SellTicket sellTicket = new SellTicket();
        Thread t1 = new Thread(sellTicket, "窗口一");
        Thread t2 = new Thread(sellTicket, "窗口二");
        Thread t3 = new Thread(sellTicket, "窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

问题: 一票多买

数据安全问题解决

为什么会出现问题?

  • 是否是多线程环境?
  • 是否是共享数据?
  • 是否有多条语句操作共享数据

如何解决?

  • 让程序没有上述安全问题环境
同步代码块

锁多条语句操作共享数据,可以使用同步代码块实现

synchronized

public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();
    @Override
    public void run() {
        for(;;) {
            synchronized (obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖了第"+ (100 - tickets + 1) + "票");
                    tickets --;
                }
            }
        }
    }
}
  • 同步的好处:解决了多线程的数据安全问题
  • 同步的弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序运行的效率
同步方法
public class SellTicket implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();
    private int curr = 0;
    @Override
    public void run() {
        for(;;) {
            if (curr % 2 == 0) {
                synchronized (this) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + ":卖了第"+ (100 - tickets + 1) + "票");
                        tickets --;
                    }
                }
            } else {
                sellTicket();
            }
            curr ++;
        }
    }
    /**
     * 同步方法的锁是 this
     * 步静态方法的锁对象是 类名.class
     */

    private synchronized void sellTicket () {
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖了第"+ (100 - tickets + 1) + "票");
            tickets --;
        }
    }
}

线程安全类

  • Stri ngBuffer

    • 线程安全,观变的字符序列
    • 从版本JDK5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它 更快,因为它不执行同步
  • Vector

    • 从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。与新的集合实现 不同,Vector被同步。如果不需要线程安全的实现,建议使用ArrayList代替Vector
  • Vector

    • 该类实现了一个哈希表,它将键映射到值。任^非null对象都可以用作键或者值
    • 从Java 2平台v1.2开始,该类进行了改进,实现了MapJ妾口,使其成为Java Collections Framework的成员。 与新的集合实现不同,Hashtable被同步。如果不需要线程安全的实现,建议使用HashMapf^替Hashtable

    Vector 和 Vector 不常用,被 Collections.synchronizedList 替代

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

Lock 锁

package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockClass implements Runnable {
    private int tickets = 100;
    /**
     * lock() 获得锁
     * unlock() 释放锁
     */
    private Lock lock = new ReentrantLock();
    
    @Override
    public void run() {
        for (;;) {
            try {
                lock.lock();
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖了第" + (100 - tickets + 1) + "票");
                    tickets--;
                }
            } finally {
                lock.unlock();
            }
        }
    }
}
package lock;

public class LockDemo {
    public  static void main(String[] args) {
        LockClass lc = new LockClass();
        Thread t1 = new Thread(lc, "窗口一");
        Thread t2 = new Thread(lc, "窗口二");
        Thread t3 = new Thread(lc, "窗口三");
        t1.start();
        t2.start();
        t3.start();
    }
}

生产者&消费者

为了体现生产和消费过程中等待和唤醒,Java提供了几个方法供我们使用,这几个方法在Object中

Object类的等待唤醒方法

方法名说明
void wait()导致当前线程等待,直到另T线程调用该对象的notify0方法或notifyAIIO方法
void notify()唤醒正在等待监视器的单个线程
void notifyAII()唤醒正在等待监视器的多个线程

生产者类

public class Producer implements Runnable {
    private Box box;

    public Producer(Box box) {
        this.box = box;
    }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            box.put(i + 1);
        }
    }
}

消费者类

public class Customer implements Runnable {
    private Box box;

    public Customer(Box box) {
        this.box = box;
    }

    @Override
    public void run() {
        for (; ;) {
            box.get();
        }
    }
}

奶箱类

public class Box {
    private int milk; // 第几瓶奶
    private boolean state = false; // 邮箱状态

    public synchronized void put(int milk) {
        // 如果有牛奶等待消费
        if (state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 没有牛奶,生产
        this.milk = milk;
        System.out.println("工人,生产了第"+this.milk+"瓶奶");
        // 生产完毕
        state = true;
        // 唤醒其他等待的线程
        notifyAll();
    }
    public synchronized void get () {
        // if 没有牛奶,等待生产
        if (!state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 有牛奶,消费牛奶
        System.out.println("消费者,购买第"+this.milk+"瓶奶");
        state = false;
        // 唤醒其他等待的线程
        notifyAll();
    }
}

main

public class BoxDemo {
    public  static void main(String[] args) {
        Box box = new Box();
        Producer producer = new Producer(box); // 生产者
        Customer customer = new Customer(box); // 消费者
        Thread tp = new Thread(producer);
        Thread tc = new Thread(customer);
        tp.start();
        tc.start();
    }
}

网络编程

端口号:用两个字节表示的整数,它的取值范围是0〜65535。其中,0~1023之间的端口号用P些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。

InetAddress

该类表示 Internet 中的(Ip)地址

public class InetAddressDemo {
    public  static void main(String[] args) throws UnknownHostException {
        InetAddress address = InetAddress.getByName("DESKTOP-GMIO664");
        // 获取主机名
        String name = address.getHostName();
        System.out.println(name);
        // 获取主ip地址
        String ip = address.getHostAddress();
        System.out.println(ip);
    }
}

UDP 发送数据

public class UDPsendDemo {
    public  static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket();
        byte[] bytes = "hello".getBytes();
        DatagramPacket dp = new DatagramPacket(
                bytes, 
                bytes.length,
                InetAddress.getByName("192.168.43.125"), 
                10000
        );
        ds.send(dp);
        ds.close();
    }
}

UDP 接收数据

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiveDemo {
    public  static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket(10000); // 10000 -> 端口
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        ds.receive(dp);
        byte[] datas = dp.getData();
        int len = dp.getLength();
        String dataString = new String(datas, 0, len);
        System.out.println(dataString);
        ds.close();
    }
}

TCP 发送数据

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpReceiveDemo {
    public  static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10001);
        Socket s = ss.accept();
        InputStream is = s.getInputStream();
        byte[] bys = new byte[1024];
        int len = is.read(bys);
        String str = new String(bys, 0, len);
        System.out.println(str);
        ss.close();
        s.close();
    }
}

TCP 接收数据

public class TcpSendDemo {
    public  static void main(String[] args) throws IOException {
        Socket s = new Socket(InetAddress.getByName("192.168.137.1"), 10001);
        OutputStream os = s.getOutputStream();
        os.write("hello, -=-=-=-".getBytes());
        s.close();
    }
}

Lambda

在数学中,函数就是有输入量,输出量的一套计算方案,也就是“拿数据做操作”

面向对象思想强调“必须通过对象的形式来做事情”

函数式思想则尽量忽路面向对象的复杂语法:“强调做什么,而不是以什么形式去做"

而我们要学习的Lambda表达式就是函数式思想的体现

package lambda;

public class Demo {
    public  static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程启动了");
            }
        }).start();
        // lambda 写法
        new Thread(() -> {
            System.out.println("lambda->多线程启动了");
        }).start();
    }
}

使用前提

  • 有一个接口
  • 接口中有且只有一个抽象方法

e.g

package lambda;

public interface Eatable {
    void eat();
}
package lambda;

public class EatableImpl implements Eatable {
    @Override
    public void eat() {
        System.out.println("Java好,Java对象好");
    }
}
public class EatableDemo {
    public  static void main(String[] args) {
        Eatable e = new EatableImpl();
        useEatable(e);

        //  匿名内部类
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("Java好,Java对象好");
            }
        });
        // lambda
        useEatable(() -> {
            System.out.println("Java好,Java对象好");
        });
    }
    private static void useEatable (Eatable e) {
        e.eat();
    }
}

带参有返回值

public interface Addable {
    int add(int x, int y);
}
public class AddableDemo {
    public static void main(String[] args) {
        useAddable((int x, int y) -> {
            return x + y;
        });
        /*
        * 参数类型可以省略
        */
        useAddable((x, y) ->  x + y);
    }
    private static void useAddable(Addable a) {
        int sum = a.add(2, 3);
        System.out.println(sum);
    }
}

方法引用

public class Demo {
    public  static void main(String[] args) {
        useFlyable((String s) -> {
            System.out.println(s);
        });
        useFlyable(System.out::println); // ::  方法应用
    }
    private static void useFlyable(Flyable f) {
        f.fly("test");
    }
}

类方法引用

public interface Converter {
    int converter(String s);
}
public class Test {
    public static int fn(String s) {
        return Integer.parseInt(s);
    }
    public int fn2(String s) {
        return Integer.parseInt(s);
    }
}
public class ConverterDemo {
    public static void main(String[] args) {
        use(s -> Integer.parseInt(s));
        // 类方法引用
        use(Integer::parseInt);
        // 只能引用静态方法
        use(Test::fn);
        
        use(s -> new Test().fn2(s));
    }
    private static void use(Converter c) {
        System.out.println(c.converter("666"));
    }
}

引用构造器

public class Student {
    private String name;
    private int age;
    public Student (String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}
public interface StudentBuilder {
    Student build(String name, int age);
}
public class StudentDemo {
    public static void main(String[] args) {
        useStudentBuilder((name, age) -> new Student(name, age));
        useStudentBuilder(Student::new);
    }
    private static void useStudentBuilder(StudentBuilder sb) {
        Student s = sb.build("阿卜杜", 18);
        System.out.println(s.getName() + "," + s.getAge());
    }
}

函数式接口

有且只有一个抽象方法的接口

public interface Flyable {
    void fly(String s);
}
public class Demo {
    public  static void main(String[] args) {
        Flyable f2 = s -> {
            System.out.println(s);
            System.out.println("函数式接口");
        };
        f2.fly("hello");
    }
}

接口中默认方法

当接口有多个实现类,需要升级(新增方法)接口时,

如果用普通抽象方法,所有实现类都必须重写新增的抽象方法;

这时,就可以用默认方法,

public interface Addable {
    int add(int x, int y);
    /*
    * 1. 默认方法可以有实现代码
    * 2. 可以被重写
    * 3. 不是必须重写
    */
    default void show () {
        System.out.println("看一下");
    }
    
    default int add2 (int x, int y) {
        return x + y;
    }
}

接口中静态方法

public interface Addable {
    int add(int x, int y);
    // 静态方法
    public static void show2() {
        System.out.println("看两下");
    }
}
public class AddableDemo {
    public static void main(String[] args) {
    	/*
    	 * 接口中的静态方法只能被接口调用
    	 */
        Addable.show2();
    }
}

接口中私有方法

public interface Addable {
    int add(int x, int y);

    default void show1 () {
        // System.out.print("看");
        show();
        System.out.print("一");
        System.out.println("下");
    }
    default void show2() {
        // System.out.print("看");
        show();
        System.out.print("两");
        System.out.println("下");
    }
    // 接口私有方法,JDK9及以上才能使用
    private void show() {
        System.out.print("看");
    }

}

类加载器

类加载

当程序要使用某个类时,如果该类还未被加载到内存中,

则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。

如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化

  • 类的加载

    • 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
    • 任何类被使用时,系统都会为之建立一个java.lang.Class对象
  • 类的连接

    • 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致

    • 准备阶段:负责为类的类变量分配内存,并设置默认初始化值

    • 解析阶段:将类的二进制数据中的符号引用替换为直接引用

  • 类的初始化

    • 在改阶段,主要就是对类变量进行初始化
  • 类的初始化步骤

    • 假如类还未被加载和连接,则程序先加载并连接该类
    • 假如该类的直接父类还未被初始化,则先初始化其直接父类
    • 假如类中有初始化语句,则系统依次执行这些初始化语句
      **注意:**在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
  • 类的初始化时机:

    • 创建类的实例调用类的类方法
    • 访问类或者接口的类变量,或者为该类变量赋值
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类

加载器

  • 类加载器的作用

    • 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
    • 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
  • JVM的类加载机制

    • 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
    • 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
    • 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
    • 类加载器的继承关系: System的父加载器为Platform,而Platform的父加载器为Bootstrap
  • ClassLoader中的两个方法

    • static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器ClassLoader
    • getParent():返回父类加载器进行委派
public class Demo {
    public static void main(String[] args) {
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println(c); // AppClassLoader
        ClassLoader c2 = c.getParent();
        System.out.println(c2); // ExtClassLoader
        ClassLoader c3 = c2.getParent();
        System.out.println(c3); // null
    }
}

反射

在这里插入图片描述

Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展JAVA反射机制:是指在运行时去获取一个类的变量和方法信息.然后通过获取到的信息来创建对象,调用方法的一种机制.由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展

获取Class类的对象

我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象这里我们提供三种方式获取Class类型的对象

三种方法:

  • 使用类的class属性来获取该类对应的Class对象。举例: Studentclass将会返回Student类对应的Class对象
  • 调用对象的getClass()方法,返回该对象所属类对应的Class对象
    该方法是Object类中的方法,所有的Java对象都可以调用该方法
  • 使用Class类中的静态方法forName(StringclassName),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
public class GetClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<Student> c1 = Student.class; // 方便
        System.out.println(c1);

        Class<? extends Student> c2 = new Student().getClass();
        System.out.println(c2);

        Class<?> c3 = Class.forName("reflect.Student"); // 灵活
        System.out.println(c3);

        System.out.println(c1 == c2 && c1 == c3); // true
    }
}

获取构造方法

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo01 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> cs = Class.forName("reflect.Student");
        // 获取构造方法(所有public)
        Constructor<?>[] cons = cs.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
        // 获取构造方法(包括私有)
        Constructor<?>[] allCons = cs.getDeclaredConstructors();
        for (Constructor con : allCons) {
            System.out.println(con);
        }
        // 获取构造方法(对应的)
        Constructor<?> con = cs.getDeclaredConstructor();
        // 通过反射构造方法创建对象
        Object o = con.newInstance(); // Student{name='null', age=0}
        System.out.println(o);
        // 获取构造方法(对应的)
        Constructor<?> c = cs.getConstructor(String.class, int.class); // 参数类型 String 和 int
        Object obj = c.newInstance("阿卜杜拉沙拉木", 18);
        System.out.println(obj); // Student{name='阿卜杜拉沙拉木', age=18}
    }
}

暴力反射

// 获取私有构造方法
Constructor<?> con = cs.getDeclaredConstructor(String.class);
// 该方法传 true,取消权限检查
con.setAccessible(true);
// 实现私有构造方法创建对象
Object o = con.newInstance();

获取成员变量并使用

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class GetFieldsDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("reflect.Student");
        Field[] fs1 = c.getFields(); // 获取公有变量
        Field[] fs2 = c.getDeclaredFields(); // 获取使用变量
        for (Field f : fs2) {
            System.out.println(f);
        }
        Field fs3 = c.getDeclaredField("name"); // 获取指定变量
        System.out.println(fs3);
        Field fs4 = c.getField("address"); // 只能获取指定公有变量
        // 使用变量
        // 先获取无参数构造方法,创建对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        fs4.set(obj, "大理");
        System.out.println(obj);
    }
}

获取成员方法并使用

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetFnDemo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class<?> c = Class.forName("reflect.Student");
        Method [] ms = c.getDeclaredMethods();
        for (Method m : ms) {
             System.out.println(m);
        }
        // 使用
        Method m = c.getDeclaredMethod("setName", String.class);
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();
        m.invoke(obj, "Hello");
        System.out.println(obj); // Student{name='Hello', age=0, address='null'}
    }
}

绕过泛型检查

在 ArrayList array 中添加 String类型数据

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class Fx {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        ArrayList<Integer> array = new ArrayList<Integer>();
        array.add(1);
        array.add(2);
        // 反射 拿到  ArrayList<> 中的add()
        Class<? extends ArrayList> c = array.getClass();
        Method m = c.getMethod("add", Object.class);
        m.invoke(array, "hello");
        System.out.println(array); // [1, 2, hello]
    }
}

运行指定配置文件

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class FileToRun {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 加载数据
        Properties prop = new Properties();
        FileReader fr = new FileReader("src\\class.txt");
        prop.load(fr);
        fr.close();
        // 获取类名, 和方法名
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");
        // 获取类,并创建对象
        Class<?> c = Class.forName(className);
        Object obj = c.newInstance();
        // 获取方法并调用
        Method m = c.getDeclaredMethod(methodName);
        m.invoke(obj);
    }
}

class.txt

className=reflect.Student
methodName=study

模块化

枚举类

自定义枚举类

package enums;

public class Light {
    // 红绿灯,创建三个实例
    public static final Light RED = new Light("红");
    public static final Light GREEN = new Light("绿");
    public static final Light YELLOW = new Light("黄");


    private String name;

    private Light(String name) { // private 避免外界创建
        this.name = name;
    }
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Light{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class Demo {
    public static void main(String[] args) {
        Light r = Light.RED;
        System.out.println(r); // Light{name='红'}
        Light g = Light.GREEN;
        System.out.println(g.getName()); // 绿
        Light y = Light.YELLOW;
        System.out.println(y); // Light{name='黄'}
    }
}

enum

package enums;

public enum Light2 {
    RED("红"), GREEN("绿"), YELLOW("黄");
    private String name;

    private Light2(String name) {
        this.name = name;
    }
    public String getName () {
        return name;
    }

    @Override
    public String toString() {
        return "Light2{" +
                "name='" + name + '\'' +
                '}';
    }
}
package enums;

public class Demo2 {
    public static void main(String[] args) {
        Light2 r = Light2.RED;
        System.out.println(r); // Light2{name='红'}
        System.out.println(r.getName()); // 红
        // 枚举在switch中的使用
        switch (r) {
            case RED:
                System.out.println("红");
                break;
            case GREEN:
                System.out.println("绿");
                break;
            case YELLOW:
                System.out.println("黄");
                break;
        }
    }
}

BigDecimal 小数精确运算

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2); // 0.30000000000000004
        System.out.println(0.1 - 0.2); // -0.1
        System.out.println(0.1 * 0.2); // 0.020000000000000004
        System.out.println(0.1 / 0.2); // 0.5

        BigDecimal bd1 = new BigDecimal("0.1");
        BigDecimal bd2 = new BigDecimal("0.2");
        System.out.println(bd1.add(bd2)); // 加 0.3
        System.out.println(bd1.subtract(bd2)); //减 -0.1
        System.out.println(bd1.multiply(bd2)); // 乘 0.02
        System.out.println(bd1.divide(bd2)); // 除 0.5
    }
}

除法运算

package big;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class DivideDemo {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal("10.0");
        BigDecimal b2 = new BigDecimal("3.0");
        /**
         * 参数1:运算对象
         * 参数2:精确到几位
         * 参数3:舍入模式
         *      RoundingMode.UP: 进一法;
         *      RoundingMode.FLOOR:去尾法
         *      RoundingMode.HALF_UP:四舍五入
         */
       BigDecimal val = b1.divide(b2, 2, RoundingMode.HALF_UP);
       System.out.println(val);
    }
}

注解

  • 注解(Annotation)︰也叫元数据,一种代码级别的说明,它是JDK1.5及以后版本引入的一个特性

  • 它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明

  • JDK1.5之后的新特性

  • 说明程序的

    注解和注释:

    • 注解︰用来说明程序,给计算机看的
    • 注释:用来对程序进行说明的文字,给程序员看的

常见注解

  • @Override

    • 用于指定方法是重写父类的方法,只能修饰方法,不能修饰其他程序元素
    • 单独来看,可能丝毫看不出程序中@Override有何作用,因为它的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法
  • @Deprecated

    • 用于表示某个程序元素(类、方法等)已过时,当其他程序使用已过时的类、方法时,编译器将会给出警告Java 9为@Deprecated增加了两个属性:
    • since:该String类型的属性指定该API从哪个版本被标记为过时forRemoval:该boolean类型的属性指定该API在将来是否会被删除
  • @SuppressWarnings

    • 指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告
    • 使用注解来关闭编译器警告时,一定要在括号里使用name = value 的形式为该注解的成员变量设置值
    • @SuppressWarnings(value = “all”)
	@SuppressWarnings(value = "all")
	List<String> list = new ArrayList();
  • @FunctionalInterface
    • Java 8新增的,只能用来修饰接口,表示该接口是一个函数式接口
    • 函数式接口:接口中有且仅有一个抽象方法
    • Lambda表达式的使用前提:接口中有且仅有一个抽象方法

元注解

对注解进行注解的注解。也就是写在注解上面的注解

两个常用元注解:

  • @Retention

    • 只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间

    • 包含了一个RetentionPolicy类型的value成员变量,所以使用的@Retention时必须为该value成员变量指定

      @Retention中可使用的值定义在RetentionPolicy中,常用值如下:

    • RetentionPolicy.CLASS:编译器把注解纪录在class文件中,当运行Java程序时,JVM不可获取注解信息,这是默认值

    • RetentionPolicy.RUNTIME:编译器把注解纪录在class文件中,当运行Java程序时,JVM也可获取注解信息,开发中常用

    • RetentionPolicy.souRlE:注解只保留在源代码中,编译器直接丢弃这种注解

  • @Target

    • 只能用于修饰注解定义,用于指定被修饰的注解能用于修饰哪些程序单元,包含一个名为value的成员变量

    @Target中可使用的值定义在ElementType中,常用值如下:

    • @Target(ElementType.TYPE):可以用于接口、类、枚举、注解
    • @Target(ElementType.FIELD):可以用于属性字段、枚举的常量
    • @Target(ElementType.METHOD):可以用于方法
    • @Target(ElementType.PARAMETER):可以用于方法参数
    • @Target(ElementType.CONSTRUCTOR):可以用于构造函数
    • @Target(ElementType.LOCAL_VARIABLE):可以用于局部变量
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {}

自定义注解

注解的本质:

public interface MyAnnotation extends Annotation { }

  • 是一个接口,该接口默认继承Annotation接口
  • 既然是接口,那么内部定义的内容,就是接口中可以定义的内容

注解的属性

  • 属性:接口中的抽象方法

  • 格式:返回值类型属性名称()[default 默认值]

    注解属性类型可以有以下列出的类型:

    • 基本数据类型
    • string
    • 枚举类型
    • 注解类型
    • Class类型
    • 以上类型的一维数组类型
    package zhujie;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    // 元注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface UserDefined {
        String name() default "fqy";
        int age();
    }
    

注解的使用和解析

使用注解:

  • 如果注解有多个属性,则可以在注解括号中用“,”号隔开分别给对应的属性赋
  • 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值
  • 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可
  • 数组赋值时,值使用{包裹。如果数组中只有一个值,则可以省略
package zhujie;

@UserDefined(name = "ZhangSan", age = 18)
public class UserDefinedTest {}

注解的使用和解析

解析注解:

  • 获取字节码文件对象,获取谁的呢?谁使用了注解,就获取谁的
    Class c = MyAnnotationTest.class;
  • 获取字节码对象上的注解信息
    MyAnnotation annotation = c.getAnnotation(MyAnnotation.class);
  • 解析注解
    String name = annotation.name();int age = annotation.age();
package zhujie;

@UserDefined(name = "ZhangSan", age = 18)
public class UserDefinedTest {
    public static void main(String[] args) {
        /**
         * 通过反射解析注解
         */
        // 获取字节码文件对象 (谁使用了,就获取谁)
        Class<UserDefinedTest> c = UserDefinedTest.class;
        // 通过字节码文件对象获取注解信息
        UserDefined userDefined = c.getAnnotation(UserDefined.class);
        // 通过注解的属性获取对应的值
        String name = userDefined.name();
        int age = userDefined.age();
        System.out.println(name + "-" + age);
    }
}

用注解运行指定类中的指定方法

框架运行的原理

public class TestClass {
    public void show() {
        System.out.println("hello!!!");
    }
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 元注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UserDefined {
    String className();
    String methodName();
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

@UserDefined(className = "zhujie.TestClass", methodName = "show")
public class UserDefinedTest {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        /**
         * 通过反射解析注解
         */
        // 获取字节码文件对象 (谁使用了,就获取谁)
        Class<UserDefinedTest> c = UserDefinedTest.class;
        // 通过字节码文件对象获取注解信息
        UserDefined userDefined = c.getAnnotation(UserDefined.class);
        // 通过注解的属性获取对应的值
        String className = userDefined.className();
        String methodName = userDefined.methodName();

        // 通过反射获取要运行的类
        Class<?> cs = Class.forName(className);
        // 创建对象
        Object obj = cs.newInstance();
        // 获取方法
        Method method = cs.getDeclaredMethod(methodName);
        // 运行
        method.invoke(obj);

    }
}

XML

  • XML的全称为(EXtensible Markup Language),是一种可扩展的标记语言
  • 标记语言:通过标签来描述数据的一门语言(标签有时我们也将其称之为元素)
  • 可扩展:标签的名字是可以自己定义的

作用:

  • 用于进行存储数据和传输数据(把数据按照xml文件的格式存储起来,并且可以把xml文件作为数据的载体在多个系统之间进行传输)

  • 作为软件的配置文件(可以把软件在运行时所需要的一些信息按照xml文件的格式配置到文件中)

语法规则

语法规则示例代码
xml要有文档声明,文档声明必须是第一行第一列<?xml version="1.0" encoding="utf-8"?>
xml必须要存在一个根标签,并且有且仅有一个
xml文件中可以定义注释信息
xml文件中可以存在以下特殊字符<>
xml文件中可以存在CDATA区<![CDATA[a]]

文档声明属性说明:

  • version :必须的,声明当前xml文件的版本。一般我们使用的都是1.0
  • encoding: 不是必须的,字符集.是使用浏览器打开的时候采用的默认的字符集的编码
  • standalone:不是必须的,描述XML文档是否需要依赖其他的文件
<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <student id="001">
        <name>zhangSan</name>
        <age>18</age>
    </student>
    <student id="002">
        <name>liSi</name>
        <age>19</age>
    </student>
</students>

XML解析思想

  • 所谓的解析就是从xml中获取到数据
  • 常见的解析思想:DOM (Document Object Model)文档对象模型,就是把文档的各个组成部分看做成对应的对象

在这里插入图片描述

XML解析技术
针对这种解析思想,不同的公司提供了不同的API的实现

常见XML解析技术:

  • JAXP: SUN公司提供的一套XML的解析的API
  • JDOM: 开源组织提供了一套XML的解析的APl-jdom
  • DOM4](常用): 开源组织提供了一套XML的解析的APl-dom4j
  • pull: 主要应用在Android手机端解析XML
<?xml version="1.0" encoding="UTF-8" ?>
<students>
    <student id="001">
        <name>zhangSan</name>
        <age>18</age>
    </student>
    <student id="002">
        <name>liSi</name>
        <age>19</age>
    </student>
</students>

学生类

package xml;

public class Student {
    private String id;
    private String name;
    private String age;

    public Student(){}

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

    public String getId() { return id; }

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

    public String getName() { return name; }

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

    public String getAge() { return age; }

    public void setAge(String age) { this.age = age; }
    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
package xml;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

public class Demo {
    public static void main(String[] args) throws FileNotFoundException, DocumentException {
        // 创建解析器
        SAXReader saxReader = new SAXReader();
        // 获取根节点对象
        Document document = saxReader.read(new FileInputStream("src\\xml\\student.xml"));
        // 根标签对象
        Element rootElement = document.getRootElement();
        // 遍历集合得到每一个学生元素
        List<Element> elements = rootElement.elements("student");

        List<Student> list = new ArrayList<>();
        for (Element studentElement : elements) {
            // 获取属性
            Attribute attribute = studentElement.attribute("id");
            // 获取属性值 id
            String id = attribute.getValue();

            // 获取name元素
            Element nameElement = studentElement.element("name");
            // 对应的值
            String name = nameElement.getText();

            Element ageElement = studentElement.element("age");
            String age = ageElement.getText();

            list.add(new Student(id, name, age));
        }
        for (Student s : list) {
            System.out.println(s);
        }
    }
}

XML 文档约束

  • xml文件最常见的作用就是作为软件的配置文件,那么解析xml文件的代码一般都是提前编写好的,而我们程序员只需要编写xml文件就可以了,但是大家试想一下,如果在编写这个xml文件时没有按照解析的要求去编写,而是随意去定义标签,那么再使用解析代码的时候势必就会出现问题

    如何限定程序员在xml文件中去定义哪些标签呢?

    • 我们就需要了解一下xml文件的约束技术

    • 简单来说: xml文档约束技术,就是用来限定xml文件中可使用的标签以及属性

      xml文件常见的约束技术有两种:

      • dtd
      • schema

单元测试 Junit

  • 在程序中,一个单元可以是一个完整的模块,但它通常是一个单独的方法或者程序

  • 在面向对象的编程中,一个单元通常是整个界面,例如类,但可能是单个方法

  • JUnit是一个Java编程语言的单元测试框架

  • 通过先为最小的可测试单元编写测试,然后编写这些单元之间的复合行为,就可以为复杂的应用程序建立全面的测试

单元测试的优点

​ 自己编写main方法测试存在的问题:

  • 无法得到测试的结果报告,需要程序员自己去观察测试是否成功

  • 如果一个测试方法失败了,其他方法测试会受到影响

  • 无法一键完成全部模块的全部方法的测试

    单元测试的优点:

  • 可以生成全部方法的测试报告。如果执行结果是绿色的,那么表明测试通过;如果执行结果是红色的,表明测试不通过

  • 单元测试中,某个方法测试失败了,不影响其他测试方法的测试

  • 可以一键执行全部测试方法

单元测试快速入门

编码约定

  • 类放在test包中
  • 在test包下创建和原包名相同的包
  • 类名用XxxTest结尾
  • 方法用testMethod命名

步骤

  1. 和src同级建立test目录,并修改该目录为Test Sources Root
  2. 在test目录下建包com.xxxx
  3. 在com.itheima包下新建类:MyMathTest
  4. 在类中编写第一个测试方法: testAdd()
    方法修饰符: public void testAdd(){}
  5. 如何让该方法称为Junit中的测试方法呢?
    导入junit-4.12.jar和hamcrest-core-1.3.jar包
    然后再方法上添加@Test注解
  6. 运行测试方法
  7. 使用断言改进测试方法的运行
;