Java 学习记录

文章目录

一.基本概念

1.1 访问修饰符

用于控制程序的其他部分对这段代码的访问级别

  • public:
    Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。
  • private:
    Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
  • protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、
    属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
  • default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访

问。

1.2 命名规则

  1. java的类名的命名规则:以字母开头,后面可以跟数字任意组合。
// An highlighted block
public class Java_001 
  • 类名是大写字母开头,并且采用驼峰命名法:YangChenXi
  • 变量的命名规则:第一个字母小写,后面的单词第一个字母大写getMyName

1.3 数据类型

java是强类型语言,意味着必须为每一个变量声明一种类型。

1.基本数据类型

类型大小
int4字节
short2字节
long8字节
byte1字节

java中没有无符号的形式(unsigned)的int,整形允许是负数
浮点型

类型大小
float4字节
double8字节

char类型
char类型表示单个字符

2.引用数据类型(按引用传递时的参数类型)

  • 数组
  • 接口

1.4变量

java中所有的变量分为:

  1. 成员变量
  2. 局部变量。

-成员变量包括:

a) 实例变量

b)类变量(以static修饰)

  • 访问:实例变量是通过定义类的对象来访问。类变量可以通过类或类对象来访问。

  • 生存周期 :实例变量与类对象生存周期共存亡。类变量与类共存亡。

  • 变量修改:多个对象指向不同的实例变量堆内存,即实例变量的值只与对象相关。多个对象指向同一个类变量的堆内存,即类变量的值与类对象无关,为最后一次修改的值。

1.5设置常量:

  1. 常量名全大写
  2. 常用final关键字只是常量,个人感觉类似c里面的const final int a = 1234;
  3. 如果希望类里面的多个方法都可以用这个常量,可以用关键字static final设置。
public class test01{
public static final double ycx = 11.22public static void main(String[] args)
{

}
}

关键字static final定义的类常量需要在main方法的外部。

1.6 对象与对象变量(即引用)

在这里插入图片描述

二.字符串

2.1 定义

  1. 定义方法:
  • 字符串字面量直接定义
String s1 = "Hello world!";
  • new 方法
String s2 = new String();
s2 = "Hello world!";

这里有一些字符串的基本判断

        String p1 = new String(s2);
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s2 == p1);
        System.out.println(s2.equals(p1));

答案是true true false true
原因是编译器编译的时候会进行优化,s1和s2的字符串相当,在字符串池中就相当于一个对象被引用了两遍,两个引用肯定也相等了。而p1则是新建了(new)了一个对象,所以p1和s2引用的不是一个对象。

  • 使用字节数组中的所有字节,根据当前的默认编码UTF-8转换为字符串
        byte[] Byte = {65,66,67,97,98,99};
        String s3 = new String(Byte);
        System.out.println(s3);
        s3 = new String(Byte,2,3);//从第2个字符(0,1,2)开始,过三个结束
        System.out.println(s3);

        //getbytes方法可以把字符串转换为字符数组
        Byte = "Hello 杨晨曦".getBytes();
        System.out.println(Arrays.toString(Byte));
        String s4 = new String(Byte,"GBK");
        System.out.println(s4);


//输出:
ABCabc
Cab
[72, 101, 108, 108, 111, 32, -26, -99, -88, -26, -103, -88, -26, -101, -90]
Hello 鏉ㄦ櫒鏇�
  • 根据字符数组创建字符串
char[] test01 = {'A','B','c','杨',30001,30020};
String s5 = new String(test01);
System.out.println(test01);
System.out.println(s5);
//输出:
ABc杨由畄
ABc杨由畄

java中的字符串对象是不可变的,要想改变并且生成新的字符串,需要拼接的。

2.2 字符串的常用方法:

  1. charAt
String c = "abcd杨晨曦";
System.out.println(c.charAt(3));

输出:
d
  1. compareTo字符串大小的比较
System.out.println(c.compareTo("abcde"));

输出:
26371
  1. format进行格式化
System.out.println(String.format("姓名 :%s,年龄 : %d,工资:%f,是否是经理:%b","杨晨曦",20,10000000.0,false));

输出:
姓名 :杨晨曦,年龄 : 20,工资:10000000.000000,是否是经理:false
  1. index,substring的使用
String text = "D:\\Code\\java1\\src\\Java核心技能\\学习文件\\Java_016字符串.java";
int lastlashindex = text.lastIndexOf('\\');//
System.out.println(lastlashindex);
String folder = text.substring(0,lastlashindex);
System.out.println(folder);

输出:
31
D:\Code\java1\src\Java核心技能\学习文件
  1. trim()去点字符串的前后空格
String s6 = "   ycx  is the best    ";
s6 = s6.trim();
System.out.println(s6);

输出:
ycx  is the best
  1. 字符串的数据类型转换valueOf
int num = 123;
String s7 = " " + num;//第一种方式,但是每一次都会生成一个新的对象,不适合多次连续使用。
s7 = String.valueOf(num);
System.out.println(s7);

输出:
123

2.3 拼接

  1. ‘+’ java允许使用+号连接字符串,但是效率较低。所以这种方法常用于输出的时候,如: 并且数据类型自动转换为String
String ycx = "man";
System.out.println("ycx is the best"+ycx);
  1. concat
String str1 = "ycx";
String str2 = "is the best";
str1 = str1.concat(str2);
  1. 可变字符串:
    StringBulider/StringBuffer的初始容量都是16,StringBulider是单线程的,StringBuffer不是线程安全的(多线程)
    StringBuilder s8 = new StringBuilder();

    s8.append("aaa");
    s8.append("bbb");
    s8.append("cc");
    System.out.println(s8);
    

    }

2.4 类型转换

String和int的转换
String 字符串转整型 int 有以下两种方式:

1.Integer.parseInt(str)
2.Integer.valueOf(str).intValue()
    public static void main(String[] args) {
        String str = "123";
        int n = 0;
        // 第一种转换方法:Integer.parseInt(str)
        n = Integer.parseInt(str);
        System.out.println("Integer.parseInt(str) : " + n);
        // 第二种转换方法:Integer.valueOf(str).intValue()
        n = 0;
        n = Integer.valueOf(str).intValue();
        System.out.println("Integer.parseInt(str) : " + n);
    }

int转换为String
整型 int 转 String 字符串类型有以下 3 种方法:

1.String s = String.valueOf(i);
2.String s = Integer.toString(i);
3.String s = "" + i;
public static void main(String[] args) {
    int num = 10;
    // 第一种方法:String.valueOf(i);
    num = 10;
    String str = String.valueOf(num);
    System.out.println("str:" + str);
    // 第二种方法:Integer.toString(i);
    num = 10;
    String str2 = Integer.toString(num);
    System.out.println("str2:" + str2);
    // 第三种方法:"" + i;
    String str3 = num + "";
    System.out.println("str3:" + str3);

2.5 大小写转换

toLowerCase() 方法可以将字符串中的所有字符全部转换成小写,而非字母的字符不受影响
toUpperCase() 则将字符串中的所有字符全部转换成大写,而非字母的字符不受影响

    String str="abcdef 我 ghijklmn";
    System.out.println(str.toLowerCase());    // 输出:abcdef 我 ghijklmn
    System.out.println(str.toUpperCase());    // 输出:ABCDEF 我 GHIJKLMN
    

2.6 替换

  1. replace() 方法用于将目标字符串中的指定字符(串)替换成新的字符(串) 全替换
    String words = "hello java,hello php";
    System.out.println("replace(\"hello\",\"你好\")结果:"+words.replace("hello","你好 "));
    结果:你好 java,你好 php
  1. 替换第一个出现的指定字符串replaceFirst() 方法
    String words = "hello java,hello php";
    String newStr = words.replaceFirst("hello","你好 ");
    System.out.println(newStr);    // 输出:你好 java,hello php

三.输入与输出

3.1 输入

通过控制台进行输入,需要先构造一个与“标准输入流”System.in相关的Scanner对象
Scanner类包含在java.util.Scanner包中

Scanner input = new Scanner(System.in)

之后就可以用Scanner类的各种方法了,如:

  • int age = input.nextIine();
  • nextIine()读取下一行的内容
  • next()读取下一个单词,以空格为分隔符
  • nextInt/nextDouble 读取下一个整数/浮点数*

四. 数组

4.1 数组的定义

  1. 定义变量
type[] arrayName;    // 数据类型[] 数组名;

type arrayName[]的可读性很差

  1. 分配空间 声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。
    Java 中可以使用 new 关键字来给数组分配空间
arrayName = new type[size];    // 数组名 = new 数据类型[数组长度];
  1. 也可以一步进行声明与分配:
type[] arrayName = new type[size]; 

4.2 二维数组

在 Java 中二维数组被看作数组的数组,即二维数组为一个特殊的一维数组,其每个元素又是一个一维数组

  1. 定义:type[][] arrayName; // 数据类型[][] 数组名;
  2. 初始化:
int[] [] n1 = {num1,num2,num3};
        System.out.println(Arrays.deepToString(n1));

        int[][] data = new int[5][];
        data[0] = new int[5];//不用使用简写
        data[1] = new int[]{1,2,3,4,5};
        int [] data1 = {6,7,8,9,10};
        data[2] = data1;
        System.out.println(Arrays.deepToString(data));

        int[][] data2 = new int[][]{num1,num2,num3,{100,200,300},{1000,2000,3000}};
        System.out.println(Arrays.deepToString(data2));
        int[][] data3 = {num1,num2,num3,{100,200,300},{1000,2000,3000}};
        System.out.println(Arrays.deepToString(data3));

Arrays类的deepToString方法可以打印多维数组

4.3 Arrays类

        int[] num = {10,20,40,30,69,50};
        Arrays.sort(num);//排序

        System.out.println(Arrays.binarySearch(num,69));//二分查找法找到key的值的位置
        System.out.println(Arrays.toString(num));//toString方法打印

        num = Arrays.copyOf(num,num.length*2);//copyof方法进行扩容(copyof方法创建了一个新的数组)
        System.out.println(Arrays.toString(num));
        Arrays.fill(num,100);//fill方法进行填充

        System.out.println(Arrays.toString(num));
        Arrays.fill(num,6,10,69);//增加了两个形参,分别是填充的起始位置和结束位置
        System.out.println(Arrays.toString(num));
    }
}

4.4数组的输出

  1. 通过遍历的方法
  int a[] = new int[10];
        int[] b = {1,2,3,4,5,6,7,8,9,10,};
        int x[] = {1,2,3,4,5,6,};
        for(int i = 0;i<10;i++){
            a[i] = i;
            System.out.println(b[i]);//数组的每个元素的输出需要用遍历的方式
        }
  1. 通过for each的方法`
        for(int c :a)
            System.out.println(c);//第二种遍历方法:for each,更加简便而且不用操心下标的问题
  1. 通过打印数组的方法(将数组变成一个字符串)
 System.out.println(Arrays.toString(a));//第三种打印数组的方法,将数组变成一个字符串

五.类

5.1基本操作

1.定义一个类:
class classname
{
......
}

用自己写的代码举个例子:

public class Java_007构造器 {

    public static void main(String[] args) {
        Student St1 = new Student("杨晨曦", 99);
        //var St2 = new Student("abc",90);
        St1.say_score();
        St1.say_sal();
        System.out.println(St1.getName());

    }
}
    class Student
    {
        public String name;
        public int score;//需要先声明成员变量(name)(score)的
        public double sal = 1.23;

        public String getName() {
            return name;
        }

        public Student(String name,int score){//变量参数回遮蔽同名的实例字段
            this.name = name;
            this.score = score;
            this.sal = sal;


        }
        public Student(){}
        public void say_score(){
            System.out.println(this.name);
        }
        public void say_sal(){System.out.println(this.sal);}
    }

定义了一个student类
里面的
public String name; public int score; public double sal = 1.23;是实例字段,也就是需要先声明的成员变量

  • public String getName()是一个方法
  • public Student(String name,int score)是构造器
2.构造器
  1. 构造器与类同名,通常与new结合使用。
  2. 如果提供了一个带参数的构造器,不提供不带参数的构造器那么构造对象的时候不提供参数是不合法发的。
3.this的使用
        public Student(String name,int score){//变量参数回遮蔽同名的实例字段
            this.name = name;
            this.score = score;
            this.sal = sal;

例如这一段代码,可以改进的地方是讲变量参数不要与实例字段的变量名同名即name变成aname
score变成ascore

        public Student(String aname,int ascore){//变量参数回遮蔽同名的实例字段
            this.name = aname;
            this.score = ascore;
            this.sal = sal;

this.name里面的name指的是隐式参数,是方法名前面student类定义的。

this的作用:

  • this调用本类中的属性,也就是类中的成员变量。
  • this调用本类中的其他构造方法,调用时要放在构造方法的首行。
  • 返回类对象的时候需要使用return this 返回。
4.关键字

类中的public关键字意味着任何类的任何方法都可以调用这个方法
类中的private确保只有自身的方法能够访问。

5.2 注意事项

  1. 源文件名必须和public类的名字相匹配,一个源文件中只能有一个公共类。
  2. 静态方法不能访问实例字段,因为它不能在对象上执行操作,可以访问静态字段,并且可以由类名直接调用。
  3. 方法中的局部变量必须明确的进行初始化,在类中则可以默认初始化。

5.3 匿名内部类

  1. 匿名内部类其实是:new [匿名] implements 接口。

六.继承

6.1基本定义:

用关键字extends表明正在构建的新类派生于一个已经存在的类。
通常将这个已经存在的类叫做:超类基类父类
新类称为 子类
Alt +Inter可以快速生成子类

6.2使用

例如之前定义的student类作为超类

class Student
    {
        public String name;
        public int score;//需要先声明成员变量(name)(score)的
        public double sal = 1.23;

        public String getName() {
            return name;
        }

        public Student(String name,int score){//变量参数回遮蔽同名的实例字段
            this.name = name;
            this.score = score;
            this.sal = sal;
            }
           

现在定义一个它的子类High

public class High extends Student {
    private int age;
    public High(String name,int score){
        super(name, score);
        age = 0;
    }
    public String getName(){
        String aname = super.getName();
        return aname+"high";
    }
    public void setAge(int x){
        age = x;
    }
}

里面有几点需要注意

  1. 两个类中都有public String getName() 方法,不加以说明的话,它会在子类中重复调用自己导致崩溃,解决的办法就是用super
    关键字。super关键字可以表示调用的方法是超类中的方法,以免导致错误。

  2. 在子类的构造器中 public High(String name,int score){
    super(name, score);
    age = 0;
    }

    可以看到也有一个super,这个super的作用是:因为High类的构造器不能访问Student类的私有字段,所以使用super来初始化。
    super调用构造器必须是第一条语句。

6.3多态

  1. 在java中对象变量是多态的。一个超类(student)类型的变量可以引用一个超类(student)类型的对象,也可以引用它的子类(High)类型的对象。

  2. 不能将超类的引用赋值给子类变量 High n = staff[0] p163

  3. 使用final可以阻止继承:public final class High extends Student

  4. 子类不能访问父类的私有字段

  5. 在覆盖一个方法的时候子类方法不能低于超类方法的可见性。

七.接口

7.1定义:

接口是一组操作规范或者是一个协议。接口不是类,而是对希望符合这个接口类的一组需求。
关键字: interface

public interface a{
void pi{...};

7.2特点

  1. 接口中一般只有抽象方法,不能有实例对象
  2. 接口中的方法一般默认为 public abstract
  3. 类对象要是需要使用接口需要关键字implements
    在IDEA中光标放在接口名称上按下Alt+Enter可以快速生成实现类
  4. 接口不能有实例字段但是可以直接定义常量
public interface a{
void pi{
private String a ;//错误
int x = 33//正确
};

7.3举例说明

  1. 需要先定义一个接口swim
package Java核心技能.接口2;

public interface swim {
    public void swimming();
}
  1. 定义几个使用该接口的实现类,如:猫类,就在实现类(Cat)中重写了接口中的抽象方法
package Java核心技能.接口2;

public class Cat implements swim {
    @Override
    public void swimming() {
        System.out.println("cat can't swim");
    }
}

Dog类

package Java核心技能.接口2;

public class Dog implements swim {
    @Override
    public void swimming() {
        System.out.println("dog can swim.");
    }
}

Fish类

package Java核心技能.接口2;

public class Fish implements swim {
    @Override
    public void swimming() {
        System.out.println("Fish can swim very well.");
    }
}
  1. 定义完实现类之后需要定义辅助类,就是帮助接口实现的类
package Java核心技能.接口2;

public class use_swim {
    public void p1(swim sm){
        sm.swimming();
    }
}
//这就相当于辅助类

这里就是定义了一个带参的方法,方便等一会使用

  1. 进行测试类
package Java核心技能.接口2;

public class test01 {
    public static void main(String[] args) {
        use_swim s = new use_swim();
        Dog dahuang = new Dog();
        s.p1(dahuang);
        dahuang.swimming();
        s.p1(new swim() {
            @Override
            public void swimming() {
                System.out.println("这就是匿名内部类中的重写方法");
            }
        });
    }
}

可以看到s.p1(dahuang);和dahuang.swimming();的效果是一样的。

7.4抽象类和接口的异同

  1. 相同点

在这里插入图片描述

  1. 不同点

在这里插入图片描述

在这里插入图片描述

八. 异常

8.1定义:

异常就是java程序中遇到了一些错误,如用户输入错误,设备错误,物理限制,代码错误。在java中,异常对象都是派生于Throwable类的一个类实例。如果内置的类还不能满足需求,用户也可以创建自己的异常类。
在这里插入图片描述
1.所有异常都是由Throwable继承而来的,有两个分支:Error和Exception
2.Error:这是虚拟机的错误,只能退出程序。
3.Exception:由程序引起的,是程序员可以处理的。

  • IOException:受检hou异常,又称编译时异常(必须进行预处理) Runtime
  • Excpetion:运行时异常一般包括(错误的强制类型转换,数组越界,访问null指针)

异常的表示
例:

Exception in thread "main" java.io.FileNotFoundException: d:\abc.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at Java核心技能.学习文件.Java_014异常预处理_抛出处理.fun2(Java_014异常预处理_抛出处理.java:21)
	at Java核心技能.学习文件.Java_014异常预处理_抛出处理.fun1(Java_014异常预处理_抛出处理.java:15)
	at Java核心技能.学习文件.Java_014异常预处理_抛出处理.main(Java_014异常预处理_抛出处理.java:9)

8.2预处理

预处理有两种方式,捕获预处理,抛出预处理

  1. 捕获预处理
      public static void p2(String[] args) {
        try {
            System.out.println("有语句产生了受检异常需要进行预处理");

            FileInputStream f = new FileInputStream("d:/abc.txt");

            System.out.println("如果检测到异常的话就交给catch进行处理。");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("main...end...");
    }
}
try中写方法体,如果有异常,就会把相应的异常交给catch,在catch里有相应的处理办法。try中可以有多个异常,catch中也可有多个异常处理办法。

如果文件存在并且无异常,输出为:

有语句产生了受检异常需要进行预处理
如果检测到异常的话就交给catch进行处理。
main...end...

文件不存在,输出为:

有语句产生了受检异常需要进行预处理
main...end...
java.io.FileNotFoundException: d:\abc.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)
	at java.io.FileInputStream.<init>(FileInputStream.java:138)
	at java.io.FileInputStream.<init>(FileInputStream.java:93)
	at 基础语法.Java_013异常捕获预处理.main(Java_013异常捕获预处理.java:15)

即,先执行try里面的语句,遇到异常的时候就进行catch里面的语句。最后执行finally。
2. 抛出预处理:就是谁调用的谁处理,一级一级向上抛出。

public class Java_014异常预处理_抛出处理 {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("调用fun1方法");
        fun1();
        System.out.println("main调用结束");
    }
//main方法调用fun1,出现异常,main处理。
    private static void fun1() throws FileNotFoundException {
        System.out.println("在fun1方法中调用fun2方法" );
        fun2();
        System.out.println("fun1执行结束");
    }
//定义fun2时抛出了一个异常,谁调fun2方法,谁负责处理(fun1)
    private static void fun2() throws FileNotFoundException {
        System.out.println("fun2方法调用File方法");
        FileInputStream f = new FileInputStream("d:/abc.txt");
    }

}
//一般情况下,在调用其他方法时,如果被调用的方法有受检异常,一般用捕获预处理
//在定义方法时如果方法体中有受检异常需要预处理,一般用抛出处理
  1. 两种办法的区别:
    throw用来抛出异常对象,后面跟的是异常对象,用在方法内部
    throws用来抛出异常类,后面跟的是异常类名,可以跟多个用逗号分开,用在方法上。

一般情况下,在调用其他方法时,如果被调用的方法有受检异常,一般用捕获预处理
在定义方法时如果方法体中有受检异常需要预处理,一般用抛出处理

8.3自定义异常

步骤:
//自定义异常的一般步骤:
//1.定义一个自定义类继承Exception
//2.自定义类定义两个构造方法,一般是一个带参的一个不带参
//3.通过throws抛出一个异常对象
//4.throws所在的方法通过throws声明该异常

1.定义一个Person类需要输入年龄性别名字

public class Person {
    String name;
    int age; //age应该符合合理的范围
    String gender;
    public Person(String aname,int aage,String agender){
        this.name = aname;
        this.age = aage;
        this.gender = agender;
    }

2.在测试函数中输入这三个值,

        Person p3 = new Person();//因为是调用,所以一般使用捕获处理
        p3.setAge(2000);
        p3.setName("aabbcc");
        p3.setGender("男");

但是年龄如果输入的过于离谱的话,这时候就应该在setAge中进行判断

    public void setAge(int age) throws AgeOutOfRangeException {
        if(age<=130 && age>=0){
        this.age = age;
        }
        else {
            //年龄不合法,抛出异常,需要自己定义一个年龄不合法的异常
            throw new AgeOutOfRangeException("年龄越界");
        }
    }

3.这时候我们定义AgeOutOfRangeException类(一般名字后面都是加上Exception)

package Java核心技能.学习文件.自定义异常;

public class AgeOutOfRangeException extends Exception {
    public AgeOutOfRangeException() {
        super();
    }

    public AgeOutOfRangeException(String message) {
        super(message);
    }
}
一般需要继承Exception的两个方法,一个带参(String)的一个不带参数的

4.测试函数的输出

Exception in thread "main" Java核心技能.学习文件.自定义异常.AgeOutOfRangeException: 年龄越界
	at Java核心技能.学习文件.自定义异常.Person.setAge(Person.java:44)
	at Java核心技能.学习文件.自定义异常.Test.main(Test.java:17)

8.4注意

  • 子类继承父类的时候,重写方法时不能够抛出更多的异常。

九 集合

9.1定义:

利用底层数据结构实现的框架,集合类库将接口与实现分离 。

9.2框架:

在这里插入图片描述
在这里插入图片描述

  1. 集合Collection有一个超级接口Iterable ,这是迭代器需要的接口(也许
  2. Collection有两个子接口List和Set,各自实现了不同的功能添加。 其中List集合元素的存储特点是有序(顺序)可重复(一个相同值可以存储多次) Set集合元素的存储特点是无序不可重复。
  3. List接口有几个常用的实现类:ArrayList(底层使用数组实现不用初始化数组的长度,可以动态变化,LinkedList(底层使用双向链表实现)Vector(底层使用数组实现

9.3Collection

Collection是一个接口,定义的时候需要new一个集合的实现类(例如Arraylist)

常用方法:

  • 加入元素:add()
  • 计算数组长度:size()
  • 清空数组:clear()
  • 删除指定值的元素:remove()
  • 判断元素中是否包含元素(是否为空):isEmpty()
  • contains的方法例如arr.contains(x)实质上是x调用了equals,如果x是存在arr里面的并且x的类(比如String)重写了equals方法,那就只比较内容,没有重写就只比较内存地址了。remove同理
  • 遍历方法:
  1. for each循环
  2. 迭代器进行遍历
  3. 使用一个整数的索引来访问(随机访问)
Collection arr = new ArrayList();
        Collection arr = new ArrayList();
        Collection arr2 = new ArrayList();
        arr.add(1200);//不报错的原因是自动装箱机制
        arr.add(12.34);
        arr.add(true);
        System.out.println(arr.size());//判断集合中元素的个数
        arr.clear();
        System.out.println(arr.size());
        arr.add("hello");
        arr.add("world");
        System.out.println(arr.contains("hello"));//判断集合是否包含输入的值
        arr.remove("hello");
        System.out.println(arr.contains("hello"));
        System.out.println(arr.isEmpty());//判断集合是否为空
        arr2.add(69696969);
        arr2.add("like");
        arr.addAll(arr2);//addAll可以将指定的Collection(arr2)加入到此集合{arr)中
        System.out.println(arr);
Collection c = new Collections();这种定义方法不行,因为Collection是接口,无法实例化,应该使用它的实现类

输出:

3
0
true
false
false
[world, 69696969, like]
9.3.1.List集合

List继承了Collection的方法,多了几个特殊方法

  • 获取指定对象第一次出现时的索引值:index()Of
  • 获取指定对象最后一次出现处的索引:lastindexOf()
  • 删除指定下标的元素:remove(index)
  • 修改指定位置的元素:set(index,value)
  • 向指定位置添加元素add(index,value)
  • 获取指定索引的值get(index)
        List p = new ArrayList();
        p.add(123);
        p.add(456);
        p.add(789);
        p.add(1,696969);
        System.out.println(p);
        p.clear();
        p.add("aaa");
        p.add("bbb");
        p.add("ccc");
        p.add("aaa");
        System.out.println(p.indexOf("aaa"));
        System.out.println(p.lastIndexOf("aaa"));
        p.remove(3);
        p.set(0,"aaaa");
        System.out.println(p.get(2));
        System.out.println(p);

输出:

[123, 696969, 456, 789]
0
3
ccc
[aaaa, bbb, ccc]
9.3.2.三个List常用实现类
1.ArrayList:
  1. 默认初始化容量是10(可以指定初始化容量(构造方法2))
  2. 底层是一个Object[]数组
  3. 构造方法3:new ArrayList(Collection<? extends E> c)可以是一个集合作为参数
  4. 扩容之后(使用位运算扩容),扩容之后是原来的1.5倍
  5. Arraylist元素的排序:通过Collections.sort方法并且传入数组的引用和比较器。
    注意:在传入比较器的时候需要自己重写构造方法,即return的值。
    例:
ArrayList<Integer> p1 = new ArrayList<>();
        p1.add(100);
        p1.add(30);
        p1.add(47);
        p1.add(69);
        Collections.sort(p1, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });
        for(int num : p1){
            System.out.println(num);
        }

输出:

30
47
69
100
2.LinkList:
  1. 底层是双向链表
  2. 在删减比较频繁的操作时使用LInkList,一般情况下使用ArrayList
3.Vector:(底层是数组)
  1. 默认容量是10,扩容之后是之前的2倍
  2. Vector中所有的方法都是线程同步的,都带有synchronized关键字,是线程安全的,效率比较低,使用较少。
    把非线程安全的转换为线程安全的方法:Collections(集合工具类):Collections.synchronizedList(x)
9.3.3.Set

在这里插入图片描述
特点:

  1. 存储时的顺序和去除的顺序不同
  2. 不可重复
HashSet

放到HashSet中的元素实际上是放到HashMap中的key中了
例:

        Set<String> str = new java.util.HashSet<>();
        str.add("11111");
        str.add("44444");
        str.add("22222");
        str.add("33333");
        str.add("22222");
        str.add("44444");
        for(String s : str){
            System.out.println(s);
        }

输出 (无序):

44444
33333
11111
22222
TreeSet

集合中的元素可以自动排序
例:

        SortedSet<String> str = new TreeSet<>();
        str.add("11111");
        str.add("44444");
        str.add("22222");
        str.add("33333");
        str.add("22222");
        str.add("44444");
        for(String s : str){
            System.out.println(s);
        }

输出:

11111
22222
33333
44444

9.4 Iterator接口

Iterator是一个接口,主要的作用就是对集合中的元素进行遍历。
Iterator也支持泛型:

 Iterator<Animal> it = p.iterator();
9.4.1.构造方法:
需要先存在一个集合对象
Collection p = new ArrayList();
然后构造迭代器
Iterator it = p.iterator();
9.4.2.Iterator的三个方法
hasNext()

如果仍有元素可以迭代,则返回 true。

next() :

返回迭代的下一个元素。(查找一个元素唯一的方法是调用next
在这里插入图片描述
可以认为迭代器位于两个元素之间,当调用next时,迭代器越过元素并且返回越过的元素的引用。

remove() :

从迭代器指向的集合中移除迭代器返回的最后一个元素。

        Collection p = new ArrayList();
        p.add("abc");
        p.add("efg");
        p.add(100);
        p.add(new Object());
        //遍历需要获取集合的迭代器对象
        Iterator it = p.iterator();

        while (it.hasNext()){
            Object obj = it.next();//it.next()方法使迭代器向下移动一个。
            it.remove();//调用迭代器的remove方法是正确的
            System.out.println(obj);
        }
        System.out.println(p.size());
    }

输出:

abc
efg
100
java.lang.Object@7c30a502
0

补充:

  1. 当集合元素的结构发生改变的时候,即添加(add)或删除(remove)元素的时候,迭代器必须重新获取,如果使用原来的迭代器,会报错。可以将迭代器看作是集合结构的一个快照,改变集合的时候,快照并没有发生变化,所以报错了。
  2. 在迭代集合元素的过程中,不能调用集合元素的remove方法删除元素,还是快照的类似问题。应该使用迭代器对象的remove方法(多看上面关于remove的代码)。
  3. Iterator接口的remove方法将会删除上一次调用next方法时返回的元素
    例如:删除字符串集合中的第一个元素:
Iterator<String> it = c.iterator();
it.next();
it.remove();

调用remove()之前没有next()是不合法的
例如:删除两个相邻的元素:

it.remove();
it.next();
it.remove();

9.5 Map

Map是一个接口
在这里插入图片描述

  1. Map与Collection的关系的并列的但是没有联系
  2. Map中的元素是以键值对的形式存在的(py的字典)
  3. key和value都是储存java中的对象的内存地址
  4. Map中key的特点:无序不可重复

常用方法:

  1. clear()
  2. containsKey()判断是否包含某个Key
  3. containsValue()判断是否包含某个Value
  4. keySet()返回Map集合中所有的Key
  5. values()返回所有的values的值
  6. get(Object key)返回指定Key的value值
  7. Set<Map.Entry<K,V>> entrySet() 将Map集合转换为Set集合

代码演示:

 Map<Integer,String> map = new HashMap<>();
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");
        //数字进行了自动装箱,put方法的作用是添加键值对

        //通过key获取value
        System.out.println(map.get(2));

        //通过size获取键值对的数量
        System.out.println(map.size());

        //通过key删除键值对
        map.remove(2);
        System.out.println(map.size());

        //contains()方法底层都是equals方法进行比较的
        System.out.println(map.containsKey(3));
        System.out.println(map.containsValue("wangwu"));

        //获取所有的value
        System.out.println(map.values());

        //clear()删除Map集合
        map.clear();

        //isEmpty判断是否为空
        System.out.println(map.isEmpty());


        //遍历
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");
        map.put(4,"zhaoliu");
        Set<Integer> keys = map.keySet();
        for(Integer num: keys){
            System.out.println(num + "=" + map.get(num));
        }

        //使用迭代器遍历
        Iterator<Integer> it = keys.iterator();
        while(it.hasNext()){
            Integer key = it.next();
            String value = map.get(key);
            System.out.println(key + "=" + value);
        }

        //使用Entry,变为Set类型进行遍历
        Set<Map.Entry<Integer,String>> set = map.entrySet();
        for(Map.Entry<Integer,String> node: set){
            System.out.println(node.getKey() + node.getValue());
        }

输出:

lisi
3
2
true
true
[zhangsan, wangwu]
true
1=zhangsan
2=lisi
3=wangwu
4=zhaoliu
1=zhangsan
2=lisi
3=wangwu
4=zhaoliu
1zhangsan
2lisi
3wangwu
4zhaoliu

Hashmap

HashMap是一个实现类
注意事项:

  1. 默认数组容量是16,加载因子是0.75
  2. 哈希表是数组与链表的组合,均匀分布才能发挥它的最大性能。
  3. 放在HashMap集合中的Key部分,以及放在HashSet中的元素,需要同时重写equals(不重写的话比的就是两个对象的地址,即引用)
    和hashcode的方法
  4. 在Map集合中存,以及从Map集合中取,都是先调用key的hashcode方法,然后再调用equals方法,所以需要重写的。
    拿put(k,v)举例,k.hashCode()方法返回哈希值,哈希值通过哈希算法转换为数组下标,当数组下标上面的值是null时,equals不需要执行
    注意:如果一个类的equals方法重写了,那么hashCode方法必须重写。equals的返回值和hashCode()方法返回的值必须一样。
  5. 当int类型作为key的时候,偶尔会出现输出是排序的情况。当插入的数据多了就不是有序的了。其他类型作为key,也不是有序的。
public class HashMap实现类 {
    public static void main(String[] args) {

        Map<Integer,fun1> map = new HashMap<>();
        fun1 s1 = new fun1("ycx");
        fun1 s2 = new fun1("ycx");
        map.put(1,s1);
        map.put(2,s2);
        
        //System.out.println(s1.equals(s2));、
        // 第一次因为没有重写equals所以比较的是引用,即地址,所以是false

        System.out.println(s1.equals(s2));
        //重写之后变成了true

        System.out.println("s1的HashCode:" + s1.hashCode());
        System.out.println("s2的HashCode:" + s2.hashCode());
//        重写hashCode方法之前的答案:
//        s1的HashCode:460141958
//        s2的HashCode:1163157884

        System.out.println("s1的HashCode:" + s1.hashCode());
        System.out.println("s2的HashCode:" + s2.hashCode());
    }
}

class fun1{
    private String name;
    public fun1(String aname){
        this.name = aname;
    }

    public String getName() {
        return name;
    }

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

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

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}
SortedMap
  • SortedMap是一个接口,其实现类是TreeMap,它可以自动的实现排序的功能,但是如果泛型的类型是自定义的类型,就需要自己添加方法如果不重写Compareable接口的话会进行报错。
  • Map比较的时候先看传入的比较器是不是为null,如果有比较器就实现比较器的规则比较,否则就实现compareTo方法。
  • 方法一:放在集合中的元素实现java.lang.Comparable接口
public class TreeMap {
    public static void main(String[] args) {
        TreeSet<person> tree = new TreeSet<>();
        person p1 = new person(34);
        person p2 = new person(12);
        person p3 = new person(65);
        person p4 = new person(69);
        tree.add(p1);
        tree.add(p2);
        tree.add(p3);
        tree.add(p4);
        for(person per: tree){
            System.out.println(per);
        }
    }
}
class person{
     int age;
    public person(int age) {
        this.age = age;
    }

}
  • 这里没有实现Comparable的接口,所以会出现报错:
  • Exception in thread "main" java.lang.ClassCastException: Java核心技能.学习文件.集合.person cannot be cast to java.lang.Comparable

进行修改(实现接口的时候要重写接口的方法):

public class TreeMap {
    public static void main(String[] args) {
        TreeSet<person> tree = new TreeSet<>();
        person p1 = new person(34);
        person p2 = new person(12);
        person p3 = new person(65);
        person p4 = new person(69);
        tree.add(p1);
        tree.add(p2);
        tree.add(p3);
        tree.add(p4);
        for(person per: tree){
            System.out.println(per.age);
        }
    }
}
class person implements Comparable<person>{
    //实现一个接口就要实现其方法
     int age;
    public person(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(person o) {
        return this.age - o.age;
    }
}

如果需要逆序的话,可以将返回值改为

return o.age - this.age
  • 方法二:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象。比较器是实现了Comparator接口类的实例
  • 内部匿名类的方式(注意形式以及细节):
TreeSet<person> tree = new TreeSet<>(new Comparator<person>(){
	public int compare(person o1,person o2){
	return o1.age-o2.age;
	}
});
  • 单独编写一个比较器(类的形式),比较器实现java.util.Comparator接口
    两种比较方法的概括:
    1. 在类中实现比较方法(实现借口Comparable<对应泛型>)
    2. 单独一个类,再单独写一个比较器(实现Comparator<对应泛型>

十. IO流

10.1 分类

  1. 一种方式是按照流的方向进行分类,以内存为参照,分为往内存中去,叫做输入== input== 或者read从内存中出来,叫做输出output或者 write
  2. 按照读取方式的不同,分为
    按字符的方式读取数据,一次读一个字符,是为了读取普通的文本文档而存在的,不能读取图片视频音乐等格式,甚至不能读取word,只能读取纯文本文件。
    按字节的方式读取数据,一次读取一个字节,即八个二进制编码,这种流是万能的,什么类型的文件都可以读取,包括文本文件,图片,声音文件,视频

10.2 Java中的流

10.2.1 四个抽象类

Java中的流已经写好了,主要有四个抽象类

  • java.io.InputStream
  • java.io.OutputStream
  • java.io.Reader
  • java.io.Writer
    以Stream结尾的都是字节流,以Reader/Writer结尾的都是字符流
    所有的流都实现了java.io.Closeable接口,都是可关闭的,都有close()方法
    流是内存和硬盘之间的一个通道,用完之后一定要关闭,不然会耗费很多资源
    所有的流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法
    这个刷新表示将通道中的剩余数据输出。刷新的作用就是清空管道。
10.2.2 主要的流

文件专属
Java.io.FileInputStream
Java.io.FileOutputStream
Java.io.FileReader
Java.io.FileWriter
转换流
Java.io.InputStreamReader
Java.io.OutputStreamWriter
缓冲流专属
Java.io.BufferedReader
Java.io.BufferedWriter
Java.io.BufferedInputStream
Java.io.BufferedOutputStream
数据流专属
Java.io.DataInputStream
Java.io.DataOutputStream
标准输出流
Java.io.ObjectInputStream
Java.io.ObjectOutputStream
对象专属流
Java.io.PrintWriter
Java.io.PrintStream

1.文件专属

1.FileInputStream和FileOutputStream有两种读取字节的方式:

  • read()的方式,一次读取一个字节
try {
            file = new java.io.FileInputStream("E:\\学习\\myfile.txt");
            while(true){
                int readDate = file.read();
                if(readDate == -1){
                    break;
                }
                System.out.println(readDate);
            }
        }
  • read(byte[] b )一次最多读取b.length个字节
       try {
            file1 = new FileInputStream("D:\\Code\\Java_Code\\Java_进阶\\src\\用户图形界面设计\\Java_001窗体.java");
            byte[] bytes = new byte[4];//一次读取4个字节
            int readNumber = 0;
            while((readNumber = file1.read(bytes)) != -1){
                System.out.print(new String(bytes,0,readNumber));
            }

        }

这里用了String的三个参数的构造方法,原因是读取的时候比如abcdef,第一次读取abcd,第二次的ef会覆盖ab,即为efcd,所以用readNumber来记录,再用String三个参数的方法进行限定,使它正确的输出

  • write需要输入的是字节流,比如byte[] bytes = {97,98,99,100}; fos.write(bytes);
    或者String s = "我爱你"; fos.write(s.getBytes());//将一段字符串加入文件
  • 当直接new FileOutputStream(“ ”)的时候每一次写入都会覆盖之前的内容### 2.转换流的用法
    BufferedReader缓冲流的构造方法中需要传入一个抽象类Reader的流,Reader的实现类有
    在这里插入图片描述
    在这里插入图片描述
    带有缓冲区的字符输入流
    使用这个流的时候不用自定义char数组,或者说不需要自定义byte数组,自带缓冲。
    当一个流的构造方法中需要传入一个流的时候,这个被传入进来的流叫做:节点流
    外部负责包装的这个流,叫做包装流。或者处理流,对于包装流来说,只需要关闭最外层的流就行,里面的节点流会自己关闭。
    带有缓冲区的流的作用:不用
FileInputStream in = new FileInputStream("D:\\Code\\Java_Code\\Java_进阶\\src\\用户图形界面设计\\Java_001窗体.java");
            in是节点流,reader是包装流/处理流
            InputStreamReader reader = new InputStreamReader(in);
            BufferedReader bufferedReader = new BufferedReader(reader);
            //简便方法:合并
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\Code\\Java_Code\\Java_进阶\\src\\用户图形界面设计\\Java_001窗体.java")));
3.用法举例

注意异常的处理,和文件架构的构建

package IO流;

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

/*
使用FileInputStream + FileOutputStream完成文件的拷贝
拷贝的过程应该是一边读一边写
 */
public class FIleCopyTest {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("E:\\学习\\myfile.txt");
            fos = new FileOutputStream("D:\\myfile.txt");
            byte[] bytes =new byte[1024*1024];
            int readCount = 0;
            while ((readCount = fis.read(bytes)) != -1){
                fos.write(bytes,0,readCount);
            }
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.标准输出流

改变了输出的方向,从输出台输出变为向指定路径输出(即输出到文件里了)

    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("hello world!");

        PrintStream ps = new PrintStream(new FileOutputStream("D:\\time"));
        System.setOut(ps);//改变了输出的方向,从输出台输出变为向指定路径输出(即输出到文件里了)
        Date now = new Date();
        SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
        String string = sm.format(now);
        for(int i = 0;i<100;i++){
            System.out.println(string);

只输出了hello world!,剩下的输出到了文件里面。

10.3 File类

  1. File和四大家族无关,所以不能完成文件的读和写
  2. File对象指的是:文件和目录路径名的抽象表达形式
    E:\学习
    E:\学习\城乡规划\2020期中微积分考试题.pdf
    一个File对象对应的可能是目录(即文件夹),也可能是文件
  3. 主要方法
    file.exist()判断文件是否存在
    file.createNewFile()创建文件
    file.mkdir()创建目录
    file.mkdirs()创建多重目录
    file.getParent()获取父路径
    file.getAbsolutePath()获取绝对路径
    file.isDirectory()判断是否是一个目录
    file.isFile()判断是否是一个文件
    file.length()获取文件大小
    file1.lastModified()获取最后一次修改文件的时间的毫秒值,需要格式化的
    实例代码:
File file = new File("D:\\filetest");
        //判断文件是否存在
        System.out.println(file.exists());
        //如果不存在则创建文件
/*        if(!file.exists()){
            file.createNewFile();
        }*/
        //如果不存在则创建目录(文件夹)mkdir()
        if(!file.exists()){
            file.mkdir();
        }

        //mkdirs()用来创建多重目录
        File file1 = new File("D:\\a\\b\\c\\d\\e");
        if(!file1.exists()){
            file1.mkdirs();
            System.out.println(file1.lastModified());
        }
		 //File[] listFiles()获取当前目录下的所有子文件
        File f = new File("E:\\学习");
        File[] files = f.listFiles();
        for(File file:files){
            System.out.println(file);
        }

非常重要:目录的拷贝

public static void main(String[] args) {

        File srcFile = new File("D:\\Code");
        File destFile = new File("E:\\");

        copy(srcFile, destFile);
    }

    private static void copy(File srcFile, File destFile)  {
        if(srcFile.isFile()){
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                in = new FileInputStream(srcFile);
                String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath():destFile.getAbsolutePath() +"\\") +srcFile.getAbsolutePath().substring(3);
                out = new FileOutputStream(path,true);
                byte[] bytes = new byte[1024*1024];
                int readCount = 0;
                while ((readCount = in.read(bytes)) != -1){
                    out.write(bytes,0,readCount);//一定要限流
                }
                out.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            return;
        }
        File[] files = srcFile.listFiles();
        for(File file : files){
            //System.out.println(file.getAbsolutePath());
            if(file.isDirectory()){//新建的对应目录
                //System.out.println(file.getAbsolutePath());
                String src = file.getAbsolutePath();
                String dest = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath():destFile.getAbsolutePath() +"\\") +src.substring(3);
                //System.out.println(dest);
                File newFile = new File(dest);
                if(!newFile.exists()){
                    newFile.mkdirs();
                }
            }
            copy(file,destFile);
        }
    }

十一 进程与线程

11.1 基础知识

1.进程与线程:
进程就是一个应用程序,线程是一个进程中的执行场景/执行单元
一个进程可以启动多个线程
例子:在DOS窗口输入java HelloWorld之后,会先启动一个JVM进程,然后JVM会启动一个主线程调用main方法,同时再启动一个垃圾回收线程负责垃圾回收。
2.多线程并发:假设启动十个线程,会有十个栈空间,每一个栈空间之间互不干扰,各自执行各自的,这就是多线程并发。
多线程的机制就是为了提高程序的处理效率。

11.2 线程的实现方式

  • 第一种方式:编写一个类直接继承java.lang.Thread,重写run方法
public class 创建线程的方式 {
    public static void main(String[] args) {
        MyThread mythread = new MyThread();
        mythread.start();
        for(int i = 0;i<100;i++){
            System.out.println("主线程-->"+i);
        }
    }

}

class MyThread extends Thread{
    @Override
    public void run() {
        //编写程序,这段程序会运行在分支线程中
        for (int i = 0;i<100;i++){
            System.out.println("分支线程-->"+i);
        }
    }
}

注意:

  1. 启动线程,start()的作用是启动一个分支线程,在jvm中开辟一个新的栈空间
  2. 启动成功的线程会自动调用run()方法,并且在分支栈的栈底部,run和main是平级的,run在分支的底部,main在主栈的底部。
  3. 直接执行mythread.run()方法的话因为没有start(),所以不会启动线程,不会分配新的分支栈,还是在主栈中的
  • 第二种方式:编写一个类实现Runnable接口(或者直接匿名内部类
public static void main(String[] args) {
接口的方式
        MyThread2 r = new MyThread2();
        Thread thread = new Thread(r);
        String name = thread.getName();
        System.out.println(name);
//        thread.setName("aaa");
//        System.out.println(thread.getName());
        thread.start();
匿名内部类的方式
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread current = Thread.currentThread();
                for(int i = 0;i<100;i++){
                    System.out.println(current.getName() + "-->"+i);
                }
            }
        });
        thread1.start();

    }
}


class MyThread2 implements Runnable{

    @Override
    public void run() {
        Thread current1 = Thread.currentThread();
        for(int i = 0;i<100;i++){
            System.out.println(current1.getName()+"分支线程-->"+i);
        }
    }
}

11.3 运行的机制

在这里插入图片描述

在这里插入图片描述

11.4 线程的调度

在这里插入图片描述

  1. 线程的优先级:最高优先级为10,最低为1,默认为5。
    优先级高低不是谁先执行谁后执行,而是优先级高的抢到的cpu时间片多一些(10最高,1最低
 System.out.println("最高优先级" + Thread.MAX_PRIORITY);
System.out.println("默认优先级" + Thread.NORM_PRIORITY);
System.out.println("最低优先级" + Thread.MIN_PRIORITY);
最高优先级10
默认优先级5
最低优先级1

设置线程的优先级

Thread.currentThread().setPriority(1);//设置线程的优先级
  1. sleep方法:使线程阻塞
  • 静态方法
  • 参数是毫秒
  • 让当前的线程进入休眠状态,进入阻塞状态,放弃占有CPU时间片,让给其他线程使用
  • 注意,是静态方法并且是让当前线程休眠,所以在哪个线程出现就让哪个线程休眠了
    例:
Thread t = new Thread(new Run());
        t.setName("TTT");
        t.start();
        //希望五秒之后醒来
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(5*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();//
    }
}
class Run implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"+"begin");
        System.out.println(Thread.currentThread().getName());
        try {
            Thread.sleep(1000*60*60*24*365);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"-->"+"end");
    }
}

这里面的 Thread.sleep(5*1000);因为在main方法里面,所以是让main线程睡眠。

 t.interrupt();

interrupt()方法会叫醒线程的睡眠。中断睡眠的方式依靠了java的异常处理机制

  1. 终止线程:终止线程的时候可以在return之前进行数据的保存,stop()方法不能保存,直接结束了线程,所以不适用stop()方法。用一个标志值来决定线程的状态,1为真0为假。
public class 终止线程 {
    public static void main(String[] args) {
        Runnable3 r = new Runnable3();
        Thread thread = new Thread(r);
        thread.setName("t");
        thread.start();

        try {
            Thread.sleep(5*1000);
            System.out.println("ycx is smart");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //已经过时,不建议使用 thread.stop();因为会丢失数据,因为这种方式直接将线程杀死
        r.run = false;
    }
}

class Runnable3 implements Runnable{
    boolean run = true;
    @Override
    public void run() {

        for(int i = 0;i<10;i++){
            if(run) {
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else
                return;
        }
    }
}

可以在return之前进行数据保存的操作
4. 合并线程和让位线程
合并线程:thread合并到当前线程,当前线程受到阻塞,thread执行直到结束
yield():让当前线程暂停一下,俗称让位

public class 合并线程 {
    public static void main(String[] args) {
        Runnable5 r = new Runnable5();
        Thread thread = new Thread(r);
        thread.setName("t");
        thread.start();

        //合并线程,thread合并到当前线程,当前线程受到阻塞,thread执行直到结束
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main over");//这是最后一个输出的,因为thread插队了。
    }
}
class Runnable5 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i<100;i++){
            System.out.println(Thread.currentThread().getName() + "--=>" + i);
        }
    }
}
t--=>95
t--=>96
t--=>97
t--=>98
t--=>99
main over

11.5 线程的优先级

  1. 系统默认的线程优先级:从低到高分为10个等级。优先级高低不是谁先执行谁后执行,而是优先级高的抢到的cpu时间片多一些(10最高,1最低)
        System.out.println("最高优先级" + Thread.MAX_PRIORITY);
        System.out.println("默认优先级" + Thread.NORM_PRIORITY);
        System.out.println("最低优先级" + Thread.MIN_PRIORITY);
        输出:
        最高优先级10
		默认优先级5
		最低优先级1
  1. 更改线程优先级的方法
Thread.currentThread().setPriority(1);

11.6线程安全问题(重点)

1. 数据会出现安全问题的情况:
  • 多线程并发
  • 有共享数据
  • 共享数据有修改的行为(例:银行取款)
2. 解决线程安全问题的方法:
线程排队执行,不能并发。用排队执行解决线程安全的问题。即线程同步机制
  1. 两种线程同步模型:
  • 异步编程模型:线程t1和线程t2各自执行,互不干扰。就是多线程并发。
  • 同步编程模型:线程t1和线程t2,在t2执行的时候,必须等待t1执行结束,两个线程之间发生了等待关系
  1. 局部变量+常量 不会有线程安全问题,成员变量可能有线程安全问题
3.synchronized

使用synchronized解决线程安全问题

  1. synchronized小括号里面的数据是必须是多线程共享的数据,才能达到多线程排队
  • ()里面写什么在于需要哪些线程同步:假设有t1t2t3t4t5五个线程,只需要t1t2t3线程排队,t4t5不需要排队()就写t1t2t3共享的对象,对t4t5不共享,
  • 不能用局部变量作为()里面的参数。
  • “abc”在字符串常量池当中,可以作为参数
    例:

1.有一个Account类

public class Account_001 {
    private String Name;//账号
    private double balance;//余额
}

2.创建一个AccountThread线程类

class AccountThread extends Thread {
    //多个线程必须共享同一个账户对象
    private Account_001 act;


    public AccountThread(Account_001 act) {
        this.act = act;
    }

    public void run() {
        double money = 5000;
        synchronized (act) {
            act.withdraw(money);
            System.out.println(Thread.currentThread().getName() + "取款成功,余额" + act.getBalance());
        }//只将synchronized (act)包裹在act.withdraw()后面会出现余额都为0的情况是因为速度太快,t2拿到锁的时候t1还没有执行sout语句,
        // t2执行完了withdraw方法后才开始t1t2的sout。
    }
}

3.测试类

        Account_001 account_001 = new Account_001("cat---001", 10000);
        //创建两个线程
        Thread thread1 = new AccountThread(account_001);
        Thread thread2 = new AccountThread(account_001);
        //设置Name
        thread1.setName("t1");
        thread2.setName("t2");
        //开始取款
        thread1.start();
        thread2.start();

这里,synchronized可以放在Account类的withdraw()方法上,也可以用在AccountThread中run里面使用withdraw之上。
synchronized可以放在Account类的withdraw()方法上输出不正确的原因:synchronized只保证了withdraw()方法时对象被锁死,只有一个线程可以进行取款操作。但是后面的sout语句没有被锁住,所以有可能两个线程对象的withdraw()方法都执行完了才进行sout语句的执行。所以余额都变成了0。

  1. 一个对象一把锁,这把锁就是标记。
    t1和t2
    当t1先执行的时候,遇到了synchronized,这个时候自动找到小括号()里面的共享对象的对象锁,并且占有,然后执行同步代码块中的程序。
    在执行过程中一直是占有的。直到结束才会释放这把锁。然后t2占有这把锁。

  2. synchronized,用在实例方法上锁的是this。出现在实例方法上,表示整个方法体都需要同步,可能会无辜扩大同步的,运行效率会降低。

  3. 静态的synchronized则是类锁,一个类一把锁,就算有100个对象,还是一把锁。类锁:保证静态变量的安全,如果有一百个对象只有一个类锁

4 在开发中解决线程安全问题
  • 在开发中解决线程安全问题:
    synchronized会让程序的执行效率降低,用户体验不好,在不得已的情况下再选择线程同步机制
    1:尽量使用局部变量代替成员变量
    2:如果必须是实例变量,那么可以创建多个对象,这样实例变量的内存就不共享了。

  • 线程安全的其他内容

  1. 守护线程:在后台运行的线程,比如垃圾回收线程。一般守护线程是一个死循环,用户线程结束的时候守护线程自动结束。
    thread.setDaemon(ture/false)方法实现
  2. 定时器Timer()
public class 定时器Timer {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date first = simpleDateFormat.parse("2021-1-19 19:00:00");
        timer.schedule(new TimerTest(), first, 1000 * 10);//需要三个参数
    }
}
//需要编写一个定时器任务类

class TimerTest extends TimerTask{
    @Override
    public void run() {
        System.out.println("hello world");
    }
}

11.5 wait() notify()

  1. wait()和notift()不是线程对象的方法,是普通java对象的方法
  2. wait方法的作用:o.wait()让正在o对象上活动的线程进入等待状态,并且释放掉线程之前占有的o对象的锁。
  3. notify方法的作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象之前占有的锁
    在这里插入图片描述
public class 生产者模式_消费者模式 {
    public static void main(String[] args) {


        List list = new ArrayList();

        Thread thread1 = new Thread(new Producer(list));
        Thread thread2 = new Thread(new Consumer(list));

        thread1.setName("生产者线程");
        thread2.setName("消费者线程");

        thread1.start();
        thread2.start();

    }
}
class Producer implements Runnable {
    private List list;

    public Producer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (list) {
                if (list.size() > 0) {
                    try {
                        //System.out.println(System.identityHashCode(list));
                        list.wait();//wait()会释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序能够执行到这一步说明仓库是空的,可以生产了
                Object object = new Object();
                list.add(object);
                System.out.println(Thread.currentThread().getName() + "--->" + object);
                //之后就需要唤醒消费者进行消费
                list.notify();
            }
        }
    }
}
class Consumer implements Runnable{
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {

        while (true) {
            synchronized (list) {
                if (list.size() == 0) {
                    try {
                        //System.out.println(System.identityHashCode(list));
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序能够执行到这里说明仓库不是空的,进行消费
                Object object = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "--->" + object);
                list.notify();
            }
        }
    }
}

十二 反射、

1.反射的作用

  • 利用Method对象
  • 灵活的,动态的改写程序
  • 反射机制:操作字节码文件,让程序更灵活。

2.反射的构造方法

  1. 使用forName()方法
Class class1 = Class.forName("java.lang.String")
  1. 每一个对象都有getclass()方法,可以使用对象的这个方法
        String s = "abc";
        Class c2 = s.getClass();
        System.out.println(c1 == c2);

		true
  1. 可以通过class获取对象的字节码
Class class2 = String.class;
  1. 如果只想执行类里面的静态代码块,可以直接使用Class.forName()方法
public class 静态代码块 {
    public static void main(String[] args) {
        try {
            Class.forName("反射.Test1");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
//静态代码块在类加载时执行,并且只执行一次。
    }
}

class Test1 {
    static{
        System.out.println("静态代码块被执行了");
    }
}

3.通过反射来实例化对象

  1. 辅助类Test
 class Test {
    public Test(){
        System.out.println("无参构造方法");
    }
}
  1. 实例化对象
public class 通过反射实例化对象 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c = Class.forName("反射.Test");
        try {
            Object object = c.newInstance();//newInstance()调用的是无参构造方法,所以一定要写无参构造方法。
            System.out.println(object);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
  1. 使用反射机制调用方法:
  2. 反射机制调用方法的四要素:
     1.method方法
     2.o1对象
     3."ycx""666"--实参
     4.return value 返回值
    
package 反射;

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

public class 反射机制调用方法 {
    public static void main(String[] args) {
        /*
        反射机制调用方法的四要素:
        1.method方法
        2.o1对象
        3."ycx""666"--实参
        4.return value 返回值
         */
        try {
            Class c = Class.forName("反射.Test");
            Object o = c.newInstance();
            Method method = c.getDeclaredMethod("Test111", String.class, String.class);
            Object o1 = method.invoke(o,"ycx","666");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }
}


配置文件一改程序就会改变,这就是反射机制的灵活性所在,不用改动源码。

4.通过反射获取属性和方法

  1. 通过反射获取属性Field,完整代码:
public class 通过反射获取Field {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c = Class.forName("反射.Test01");
        String cName = c.getName();//完整类名
        String Cname = c.getSimpleName();//简单类名
        System.out.println(cName);
        System.out.println(Cname);
        Field[] fields = c.getFields();
        System.out.println(fields.length);//测试数组只有一个元素
        // 取出元素
        Field f = fields[0];
        String fieldName = f.getName();
        System.out.println(fieldName);//只能取出访问权限是public的

        Field[] fs = c.getDeclaredFields();//取出来了所有的属性
        System.out.println(fs.length);

        for(Field f1 : fs){
            System.out.println(f1.getType().getSimpleName());//打印的是类型名
        }
        for(Field f1 : fs){
            System.out.println(f1.getName());//打印的是字段
        }
        for(Field f1 : fs){
            System.out.println(f1.getModifiers());//打印修饰符,每一个数字都是访问修饰符的代号
        }
        for(Field f1 : fs){
            int i = f1.getModifiers();
            System.out.println(Modifier.toString(i));//打印修饰符
        }
    }
}

class Test01{
    private String name; //Field对象
    protected  int age;  //Field对象
    boolean sex;        //Field对象
    public int number;  //Field对象
}

其中Class的方法:

  • Class c = Class.forName(“反射.Test01”); :先构造一个Class对象c
  • c.getName(); : 获取完整类名 反射.Test01
  • c.getSimpleName(); : 获取简单类名Test01
  • Field[] fields = c.getFields(); : 通过getFields();方法获取Field类型对象,但是只能取出只能取出访问权限是public的。
  • c.getDeclaredFields(); : 可以取出所有的属性

其中Field的方法(Field[] fs = c.getDeclaredFields();):

  • f1.getType().getName() : 打印的是类型名java.lang.String,int,boolean,int
  • f1.getType().getSimpleName() : 打印的是简单类型名
  • f1.getName()) : 打印的是字段
  • f1.getModifiers() : 打印的是以数字表示的修饰符
  • int i = f1.getModifiers();Modifier.toString(i) :用来打印修饰符

2.同理,通过反射获取方法Method

public class MethodTest {
    public static void main(String[] args) throws ClassNotFoundException {
        Class test = Class.forName("反射.method");
        Method[] methods = test.getDeclaredMethods();
        for (Method method : methods){
            System.out.println(method.getName());//获取方法名
        }
        for (Method method : methods){
            System.out.println(method.getReturnType());//获取返回值类型
        }
        for (Method method : methods){
            int i = method.getModifiers();
            System.out.println(Modifier.toStrin
            g(i));//获取访问修饰符
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes){
                System.out.println(parameterType.getSimpleName());//获取参数列表的类型
            }
        }
    }
}


class method{
    int number;
    String string;
    public void setnumber(int i){
        this.number = i;
    }
    public void method1(){
        System.out.println("fangfa1");
    }
    public method() {
    }
}

其中Medthod的方法:

  1. Method[] methods = test.getDeclaredMethods(); : 通过getDeclaredMethods方法获取Method类型对象,获取的是所有访问权限的方法。
  2. method.getReturnType() : 获取返回值类型
  3. 获取访问修饰符的方法同Field相同,注意把数字转换为字符串
  4. Class[] parameterTypes = method.getParameterTypes(); : 通过getParameterTypes()方法获取Class[]类型的对象(即参数列表

5.反编译

  1. 通过反射机制,反编译一个类的属性Field
public class 反编译 {
    public static void main(String[] args) throws ClassNotFoundException {
        StringBuilder stringBuilder = new StringBuilder();
        //Class c = Class.forName("反射.Test01");
        Class c = Class.forName("java.lang.String");


        stringBuilder.append(Modifier.toString(c.getModifiers())+" class "+ c.getSimpleName() +"{\n");
        Field[] fields = c.getDeclaredFields();
        for(Field field : fields){
            stringBuilder.append("\t");
            stringBuilder.append(Modifier.toString(field.getModifiers()));
            stringBuilder.append(" ");
            stringBuilder.append(field.getType().getSimpleName());
            stringBuilder.append(" ");
            stringBuilder.append(field.getName());
            stringBuilder.append(";\n");
        }
        stringBuilder.append("}");
        System.out.println(stringBuilder);
    }
}

结果:

public final class String{
	private final char[] value;
	private int hash;
	private static final long serialVersionUID;
	private static final ObjectStreamField[] serialPersistentFields;
	public static final Comparator CASE_INSENSITIVE_ORDER;
}
  1. 反编译方法
public class 反编译方法 {
    public static void main(String[] args) throws ClassNotFoundException {
        StringBuilder stringBuilder = new StringBuilder();
        Class classMethod = Class.forName("java.lang.String");
        stringBuilder.append(classMethod.getModifiers() + " class " + classMethod.getSimpleName());

        Method[] methods = classMethod.getMethods();
        for(Method method: methods){
            stringBuilder.append("\t");
            int i = method.getModifiers();
            stringBuilder.append(Modifier.toString(i));
            stringBuilder.append(" ");
            stringBuilder.append(method.getReturnType().getSimpleName());
            stringBuilder.append(" ");
            stringBuilder.append(method.getName());
            stringBuilder.append("(");
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameter : parameterTypes){
                stringBuilder.append(parameter.getSimpleName());
                stringBuilder.append(",");
            }
            stringBuilder.deleteCharAt(stringBuilder.length() -1);
            stringBuilder.append("){}\n");
        }
        stringBuilder.append("}");
        System.out.println(stringBuilder);
    }
}
(节选)
17 class String	public boolean equals(Object){}
	public String toString){}
	public int hashCode){}
	public int compareTo(String){}
	public volatile int compareTo(Object){}
	public int indexOf(String,int){}
	public int indexOf(String){}
	public int indexOf(int,int){}
	public int indexOf(int){}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值