【后端开发】Java基础学习(适合有其他语言基础)

sublime小技巧

ctrl+shift+d:复制上一行

ctrl+shift+k:删除整行

(去查sublime快捷键)

直接输入main就可以显示

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

cmd小技巧

按tab键自动补全

在dos里面先javac再java,且java命令的时候不需要后缀名

回车和换行符

回车\r,换行\n

回车是将光标移动到这一行的开头,换行是将光标移动到下一行的开头

javadoc

javadoc配合文档注释可以生成文档

javadoc -d d:\\cls1277\\Java\\javadoc -author -version Hello.java

其他标签自行查看:javadoc标签文档

dos指令

dir:查看当前目录的文件内容

cd:cd /D c: 切换磁盘,直接写cd c:不会切换,需要/D开关

cd :切换到根目录

tree:查看树形目录

exit:退出dos

echo:echo hello > hello.txt 输入内容到文件

type:nul > hello.txt 创建文件

help:help cd(指令)就可以看到参数

+号的使用

都是数值,加法;有一个为字符串,拼接。

查JavaAPI的网站

https://www.matools.com/

(不止java,不止api)

编码问题

ASCII码:1个字节

Unicode码:都是2个字节

Unicode码兼容ASCII码

UTF-8:对Unicode的改进,变长编码,使用1-6个字节表示,字母占1个字节,汉字占3个字节

基本类型转换

在这里插入图片描述

byte、short和char之间不会相互自动转换

为什么byte=10不报错?具体数值赋值的时候,先判断是否在byte的范围内,若在范围内,则不报错。

int a=1; byte b=a; //报错

byte、short、char计算时,都转化为int类型运算,结果也就是int,不能往小的数据类型中存储

自动提升原则:表达式结果的类型自动提升为 操作数中最大的类型

基本数据类型转字符串

在变量后加+“”即可。

    int n = 100;
    String s = n+"";
    System.out.println(s);

字符串转基本数据类型

包装类(每个基本数据类型都对应一个包装类)调用parseXX

    String s = "100";
    int num = Integer.parseInt(s);
    System.out.println(num);

运算符

&&:短路与;&:逻辑与,(其他类似)

复合赋值运算符会进行类型转换

    byte b = 2;
    b = b + 3;

这样会报错,因为b+3会被推导为int类型,不能放到byte类型的b变量中,但是b+=3;不会报错,b=b+1;与b++同理。

关键字和保留字区别

前者是已经有的,如public,static等;后者是保留起来,以后会使用,如goto,byValue,var等。

标识符命名规范

包名:所有字母都小写

类名、接口:所有单词首字母大写(大驼峰)

变量名、方法名:第一个单词首字母小写,第二个单词开始首字母大写(小驼峰、驼峰命名法)

常量名:所有字母均大写,单词间用下划线连接

键盘输入

import java.util.Scanner;
public class Hello {
	public static void main(String[] args) {
        Scanner myScanner = new Scanner(System.in);
        String myText = myScanner.next();
        int myInt = myScanner.nextInt();
        double myDouble = myScanner.nextDouble();
        System.out.println(myText);
	}
}

原码、反码、补码

在这里插入图片描述

break和标签

public static void main(String[] args) {
    label1:
    for(int i=0; i<3; i++) {
        label2:
        for(int j=0; j<5; j++) {
            if(j==2) {
                break label1;
            }
            System.out.println("j="+j);
        }
    }
}

输出结果为j=0 j=1 因为break label1,相当于终止了外层i的循环。continue同理。

数组的使用

public static void main(String[] args) {
    //first
    int a[] = new int [5];
    int[] b = new int [5];
    //second
    int c[]; c = new int [10];
    int []d; d = new int [10];
    //third
    int e[] = {1, 2, 3};
    int[] f = {4, 5, 6};
}

数组的赋值机制

引用赋值方式

public static void main(String[] args) {
    int[] a = {1, 2, 3};
    int[] b = a;
    b[0] = 4;
    for(int i=0; i<3; i++) {
        System.out.println(a[i]);
    }
    //输出 4 2 3
}

值传递和引用传递

在这里插入图片描述

二维数组

java允许数组每一行的列数不同,见下面的third代码

	public static void main(String[] args) {
        //first
        int a[][] = new int[2][3];
        int[][] b = new int[2][3];

        //second
        int c[][]; c = new int[2][3];
        int[][] d; d = new int[2][3];

        //third
        int[][] e = new int[3][];
        for(int i=0; i<e.length; i++) {
            e[i] = new int[i+1];
            for(int j=0; j<e[i].length; j++) {
                e[i][j] = i+1;
            }
        }
        for(int i=0; i<e.length; i++) {
            for(int j=0; j<e[i].length; j++) {
                System.out.print(e[i][j]+" ");
            }
            System.out.println();
        }

        //forth
        int[][] f = {{1, 2}, {3, 4}, {2}};
	}

二维数组还可以” int [] y[] “的形式定义。

对象内存布局

在这里插入图片描述

类与对象内存分配机制

与数组相似,因为同为引用数据类型。
在这里插入图片描述

java内存分析

栈:一般存放基本数据类型(局部变量)

堆:存放对象(Cat cat,数组等)

方法区:常量池(常量,比如字符出),类加载信息

(先加载类信息,且该类只会加载一次)

方法调用机制

在这里插入图片描述

传参机制

基本数据类型:赋值;引用数据类型:赋地址。

引用数据类型为null相当于断开引用连接

public class Object {
	public static void main(String[] args) {
		Cat cat = new Cat();
		cat.name="cls"; cat.age=10; cat.color="white";
		Test test = new Test();
		test.func(cat);
		System.out.println(cat.name+" "+cat.age+" "+cat.color);
		//输出 cls 100 white
	}
}

class Cat {
	String name;
	int age;
	String color;
}

class Test {
	public void func(Cat cat) {
		cat.age = 100;
		cat = null;
		cat.name = "test";
	}
}

判断对象是否为同一个

通过输出对象的hashCode,可以简单的当作对象地址,但其实不是对象的实际地址

public class Javap {
	public static void main(String[] args) {
		Test test = new Test();
		System.out.println(test.hashCode());
	}
}

class Test {

}

或者直接对引用数据类型a和b,判断(a==b)

可变参数

public class Object {
	public static void main(String[] args) {
		Number number = new Number();
		int a = number.add(1, 3, 5, 6);
		System.out.println(a);
	}
}

class Number {
	public int add(int ... nums) {
		int res = 0;
		for(int num:nums) {
			res += num;
		}
		return res;
	}
}

可变参数的实参可以为数组,可变参数的本质就是数组。

可变参数可以与普通参数一起放在形参列表,但是可变参数一定放到最后!不能有两个可变参数!

作用域

局部变量无默认赋值,全局变量有默认赋值。

属性/全局变量可以加修饰符,局部变量不能加修饰符。

构造器

其实就是构造函数,public也可不写。

public class Object {
	public static void main(String[] args) {
		Number number = new Number(1, 3);
		System.out.println(number.num1+" "+number.num2);
	}
}

class Number {
	int num1, num2;
	public Number(int pnum1, int pnum2) {
		num1 = pnum1;
		num2 = pnum2;
	}
}

查看默认构造器

javap反编译指令!(也可直接写javap Test)
在这里插入图片描述

参数:-c:返回汇编代码;-v:输出附加信息。

自定义构造器后,默认构造器无效,此时无法使用new Test();构造器,除非显式定义无参构造器。

对象创建流程分析

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

this

在这里插入图片描述

可以使用hashCode判断this与当前对象的相等的本质。

this访问构造器语法,只能放到构造器内,必须放在第一条语句!

public class Javap {
	public static void main(String[] args) {
		Test test = new Test();
		System.out.println(test.a+" "+test.b);
	}
}

class Test {
	int a, b;
	public Test() {
		this(1, 3);
	}
	public Test(int a, int b) {
		this.a = a;
		this.b = b;
	} 
}

IDEA

out存放编译后的文件.class,src存放源文件.java

快捷键:alt+/,自动补全。alt+enter,自动导入。ctrl+alt+L,自动排版(可能与QQ的锁定热键冲突)。

运行的快捷键,我改成了ctrl+alt+N,可以在设置中自定义。

自动添加构造器,alt+insert。

查看类的继承关系:ctrl+h,将光标移动到类名上。

定位到哪个类的方法:ctrl+b,将光标移动到方法上。

自动分配变量名:在声明的后面加.var,再按回车,如:

new Scanner(System.in).var

还有很多的快捷键!

模板:main,sout,fori

如果一个项目包含很多个.java文件,不能直接快捷键运行,应该先手动run一次

查看jdk源码:ctrl+b,ctrl+鼠标左键,鼠标右键+goto

1000.for:快捷键for(int i=1; i<=1000; i++)

values.for:foreach循环values

ctrl+J:可以提示所有的快捷键

包的命名

只能包含数字、字母、下划线、小圆点,目录名不能以数字开头,不能是关键字或保留字

一般是小写字母+小圆点,一般是:com.公司名.项目名.业务模块名

常用的包:java.lang.*:基本包,默认引入;java.util.*:工具包;java.net.*:网络包,网络开发;java.awt.*:java界面开发,GUI

最好是需要什么类就导入什么类,不建议使用*导入

package的作用是声明当前类所在的包,放在(类和import的)最上面且只有一句

访问修饰符

public:对外公开

protected:对子类和同一个包中的类公开

默认:向同一个包的类公开

private:只有类本身可以访问,不对外公开

在这里插入图片描述

封装

步骤:属性私有化、提供set和get(快捷键:alt+insert->getter and setter)

继承

package com.generate;

public class Pupil extends Student {
    public void testing() {
        System.out.println(name + "testing");
    }
}
package com.generate;

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

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

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

    public void setScore(double score) {
        this.score = score;
    }
}

父类私有属性和方法不能被子类直接访问。属性可以自己写get函数,方法可以写call函数,都是public的。

子类构造器中默认有个super(),默认调用。子类的构造器(包括有参无参)会调用父类的无参构造器,若父类没有无参构造器,使用super去指定选哪个父类构造器,否则编译错误。

super在使用时,放在子类构造器的第一行。

super和this不能共存在一个构造器内,因为都要放在第一行。

Object是所有类的基类。

子类只能继承一个父类,单继承机制。

本质(注意:父类的name也分配内存):
在这里插入图片描述

查找关系:(可访问的,访问修饰符问题)就近原则,如果不能访问(private),但是找到了,会报错,不再继续向上找。

super

super.属性名:访问父类的属性(除private)

super.方法名(参数):访问父类的方法(除private)

**怎么访问父类的父类?**不能super.super,他是查找关系,如果父类有的话就不需要找父类的父类了。

和this的比较
在这里插入图片描述

方法重写

子类中方法的参数和方法名一样,子类返回类型和父类一样,或者为父类返回类型的子类。

子类方法不能缩小父类方法的访问权限。

多态

只要能体现多种状态

方法重载体现多态,方法重写体现多态

核心:对象多态
在这里插入图片描述

当运行的时候,看运行类型,不是编译类型。

向上转型:父类的引用指向子类的对象,可以调用父类的所有成员(查找),不能调用子类的特有成员(遵守访问权限,因为在编译阶段,能调用哪些是由编译类型决定的)。Animal animal = new Cat();

向下转型:(解决:父类不能调用子类的特有成员问题)Cat cat = (Cat) animal;要求animal之前就指向Cat类型对象。
在这里插入图片描述
属性没有重写一说,只看编译类型!

B instanceOf A比较操作符,判断对象B的运行类型是否A类型或A的子类型

动态绑定机制

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用

多态应用

多态数组、多态参数

Object类

所有对象都可以用它的方法

equals:和==的区别:==既可以判断基本类型,又可以判断引用类型(前者为判断值相等,后者为判断地址相等);equals:只能判断引用类型,子类往往重写,如String判断内容是否相等。

toString

返回:全类名(包名+类名)+‘@’+把hashCode转为16进制

一般重写,输出对象的属性,使用alt+insert中就有

当直接输出一个对象时,toString方法会被默认调用,System.out.println(Person);等价于输出Person.toString()

finalize

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

alt+insert中也有重写的快捷键,可以理解为C++的析构函数

好像是不推荐使用了,已经被弃用了

类变量和类方法

类变量也叫静态变量,static,在类加载的时候就生成了。方法区有个静态域中(jdk8以前),堆中类对应的class对象(jdk8及以后)

可以使用类名.类变量来访问。

类方法也叫静态方法。

静态方法中只能访问静态属性或静态方法,不能用this(super同理),因为有this就有对应的对象了。

普通方法既可以访问静态成员,又可以访问普通成员。

应用:当方法中不涉及到对象相关成员的时候,使用静态方法可以提高开发效率。(希望不创建实例就可以调用,当作工具来使用,如sqrt)

main方法

jvm在调用,main中不可以访问非静态成员(因为它是static修饰的方法)

若要访问非静态成员,要new一个main所在类的对象,再访问

代码块

属于类的成员,类似于方法,但是没有方法名,没有返回值,没有参数,只有方法体,不能显式调用,在加载类的时候隐式调用

修饰符要写只能写static,或者直接不写。

package com.codeblock;
public class Codeblock {
    public static void main(String[] args) {
        T t = new T(1, 2);
    }
}

class T {
    int A, B, C;
    {
        System.out.println("Code Block is used.");
    }
    public T(int a) {
        A = a;
        System.out.println("A");
    }
    public T(int a, int b) {
        A = a;
        B = b;
        System.out.println("A B");
    }
    public T(int a, int b, int c) {
        A = a;
        B = b;
        C = c;
        System.out.println("A B C");
    }
}

输出结果

Code Block is used.
A B

相当于对构造器的一种补充机制,可以做初始化的操作。

注意事项:

如果加了static,是静态代码块,则只会在类加载的时候执行一次;反之,为普通代码块,当且仅当创建一次对象执行一次。

如果只是使用静态成员,普通代码块不会执行。
在这里插入图片描述

构造器中先调用super(),再调用普通代码块(因为静态代码块在类加载的时候已经被执行了),再执行构造器中其他代码

创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行
④父类的构造方法
⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行
⑥子类的构造方法面试题

类什么时候被加载

创建对象实例时,创建子类对象实例时父类被加载,使用类的静态成员时

(使用子类的静态成员的时候,父类也会被加载)

单例模式

单个的实例:某个类只有一个对象实例,包括饿汉式和懒汉式

饿汉式:构造器私有化(防止用户直接new);类的内部创建对象;向外暴露一个静态的公共方法getInstance

package com.single;

public class ehan {
    public static void main(String[] args) {
        GF gf = GF.getInstance();
    }
}

class GF {
    private String name;
    //为什么是static?因为下面getInstance必须是static,且需要调用这个属性,所以这里必须有static
    private static GF gf = new GF("a");
    private GF(String name) {
        this.name = name;
    }
    public static GF getInstance() {
        return gf;
    }
}

为什么叫饿汉式?“很着急,类加载了就创建了这个对象”

懒汉式:使用的时候再创建对象。

步骤:构造器私有化;定义一个static静态属性对象;提供一个public的static方法,可以返回该类型的方法

为什么叫懒汉式?“很懒,不去用它就不去创建”

package com.single;

public class ehan {
    public static void main(String[] args) {
        BF bf = BF.getInstance();
    }
}

class BF {
    private String name;
    private static BF bf;
    private BF(String name) {
        this.name = name;
    }
    public static BF getInstance() {
        if(bf==null) {
            bf = new BF("a");
        }
        return bf;
    }
}

final

可以修饰类、属性、方法和局部变量(感觉某些情况有点像const)

在某些情况下,程序员可能有以下需求,就会使用到final:
1)当不希望类被继承时,可以用final修饰.
2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰.
3)当不希望类的的某个属性的值被修改,可以用final修饰.
4)当不希望某个局部变量被修改,可以使用final修饰.

final修饰的属性一般叫常量

如果final修饰的属性是静态的,则初始化的位置只能是:
定义时;在静态代码块;不能在构造器赋值

如果类不是final类,但是有final方法,则虽然该方法不能重写,但是可以被继承。

final不能修饰构造方法(即构造器)

final往往和static往往搭配使用效率更高,不会导致类加载。(把一个值放到类当中,但是不希望加载这个类就可以使用这个值)

抽象类

父类方法的不确定性,需要声明但不明确如何实现

当一个类存在抽象方法时,需要将该类声明为抽象类。

abstract class Test {
    int a;
    public abstract void input();
}

不能被实例化。不一定包含抽象方法。abstract只能修饰类和方法。

如果一个类继承了抽象类,必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类(实现可以只打花括号即可)

抽象方法不能使用private,final,static来修饰,这些关键字与重写相违背。(static:因为其被类名直接调用,但是抽象类无方法体)

实践:模板设计模式

接口

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。

类实现接口的时候,必须实现接口的抽象方法。

jdk7之前接口中所有方法都没有方法体。jdk8之后可以有(静态方法default、默认方法static),接口中可以有方法的具体实现。

在接口中,抽象方法可以省略abstract

package com.interface_;

public interface usb {
    public void start();
    public void end();
}
package com.interface_;

public class phone implements usb{
    @Override
    public void start() {
        System.out.println("start");
    }

    @Override
    public void end() {
        System.out.println("end");
    }
}
package com.interface_;

public class computer {
    public void work(usb _u) {
        _u.start();
        _u.end();
    }
}
package com.interface_;

public class Test {
    public static void main(String[] args) {
        phone p = new phone();
        computer c = new computer();
        c.work(p);
    }
}

接口本身是抽象的概念,不能被实例化。

接口中所有方法为public方法,不写public但也不是默认的,abstract也可以省略。

抽象类实现接口,可以不用实现接口的方法。

一个类同时可以实现多个接口。

接口名.属性名可以访问接口中的属性。

接口不能继承其他类,但是接口可以继承多个别的接口。

接口和继承

类比为:师徒和父子关系。

子类自动拥有父类的功能,如果子类需要扩展功能,可以用过实现接口的方式扩展。

实现接口是对java单继承机制的补充。

接口在一定程度上实现代码解耦

接口多态

接口类型的变量可以指向实现接口的对象(动态绑定):IA ia = new Test(); class Test implements IA(跟继承向上转型体现多态相似)

多态数组:类似于继承中的向上转型,如果需要调用特有的方法,需要instanceOf向下转型

多态传递:

package com.interface_;

public class Test {
    public static void main(String[] args) {
        IA ia = new TT();
        IB ib = new TT();
    }
}

interface IA {}
interface IB extends IA{}

class TT implements IB{

}

内部类

一个类的内部又完整的嵌套了另一个类结构,被嵌套的类叫内部类。

最大的特点:可以直接访问私有属性,体现类与类之间的包含关系。

分类:定义在外部类的方法体内:局部内部类,匿名内部类(*);成员位置上:成员内部类(没static修饰),静态内部类(用static修饰)。

局部内部类:

package com.innerclass;

public class InnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.m();
    }
}

class Outer {
    private int n = 10;
    public void m() {
        class Inner {
            private int n = 20;
            //局部内部类
            //不能添加访问修饰符,因为是局部变量;但是可以用final修饰,因为局部变量可以加final,表示不可以被继承的
            public void f() {
                System.out.println("n="+n); //可以直接访问外部类的private成员,就近原则
                //外部类在方法中创建Inner的对象,再调用类中的成员

                System.out.println("n="+Outer.this.n);
            }
        }
        Inner inner = new Inner();
        inner.f();
    }
}

匿名内部类:

该类没有名字。匿名内部类是一个对象。

基于接口的匿名内部类:

package com.niming;

public class InnerClass {
    public static void main(String[] args) {
        outerClass o = new outerClass();
        o.test();
    }
}

class outerClass {
    //基于接口的匿名内部类
    private int n = 10;
    public void test() {
        IA t = new IA() {
            @Override
            public void ok() {
                System.out.println("test!");
            }
        };
        //t的运行类型就是匿名内部类
        t.ok();
        System.out.println(t.getClass());
        //输出:outerClass$1,系统分配给匿名内部类的名字
        //匿名内部类使用一次,就不能再使用了,不是t对象!
    }
}

interface IA {
    public void ok();
}

基于类的匿名内部类:

package com.interfaceInnerClass;

public class InnerClass {
    //基于类的匿名内部类
    public static void main(String[] args) {
        outerClass o = new outerClass();
        o.test();
    }
}

class outerClass {
    private int o = 10;
    public void test() {
        //关于下面带不带花括号的讨论:
        //不带:A的运行类型是A;带:A的运行类型是outerClass$1
        //class outerClass$1 extends A {},继承
        A a = new A() {//参数会传递给A类的构造器,一般不重写A类的构造器
            //匿名内部类可以重写A类的方法
            @Override
            public void f() {
                System.out.println("o="+outerClass.this.o);
                //这里可以直接访问到o,即匿名内部类可以直接访问外部的成员包括private
            }
        };
        a.f();
        //也可以不用a对象接受,直接new了之后调用.f()
    }
}

class A {
    private int a = 10;
    public void f() {
        System.out.println("a="+a);
    }
}

若内部类和外部类重名,遵循就近原则,或者使用外部类.this.成员访问。

应用:当作实参直接传递,简洁高效

package com.innerclassusage;

public class InnerClass {
    public static void main(String[] args) {
        f(new IA(){
            @Override
            public void show() {
                System.out.println("show");
            }
        });
    }
    public static void f(IA a) {
        a.show();
    }
}

interface IA {
    public void show();
}

成员内部类:

本质就是成员

package com.sonclass;

public class InnerClass {
    public static void main(String[] args) {
        T t = new T();
        T.inner.test();
    }
}

class T {
    private int n = 10;
    public int m = 15;
    class inner {
        //可以访问外部类的所有成员,包括私有的
        //外部类访问要先创建内部类对象再调用内部类方法
        public static void test() {
            System.out.println("test!");
        }
    }
}

外部其他类访问内部类的方法:

package com.neibuclass;

public class innerclass {
    public static void main(String[] args) {
        //第一种
        T t = new T();
        T.a a = t.new a();
        a.f();
        //第二种
        T.a a2 = t.geta();
        a2.f();
        //第三种
        T.a a3 = new T().geta();
        a3.f();
    }
}

class T {
    class a {
        public void f() {
            System.out.println("cls tcl");
        }
    }
    public a geta() {
        return new a();
    }
}

如果重名,与之前的处理方法一样。

静态内部类:

唯一的区别就是有static修饰

package com.staticclass;

public class innerclass {
    public static void main(String[] args) {
        outerClass.innerClass.print();
        outerClass o = new outerClass();
        o.f();
    }
}

class outerClass {
    private int n = 10;
    private static String name = "cls tcl";
    static class innerClass {
        //只可以访问外部类static的成员,不可以访问非静态成员
        public static void print() {
            System.out.println("name="+name);
        }
        public void print2() {
            System.out.println("name2="+name);
        }
    }
    public void f() {
        innerClass.print();
        innerClass i = new innerClass();
        i.print2();
    }
}

外部其他类访问静态内部类

package com.staticclass;

public class elseclass {
    public static void main(String[] args) {
        //第一种
        A.B b = new A.B();
        b.print();
        //第二种
        b = A.getB();
        b.print();
        //第三种
        A a = new A();
        b = a.getBB();
        b.print();
    }
}

class A {
    private int n = 10;
    static class B {
        public int m = 15;
        public void print() {
            System.out.println("m="+m);
        }
    }
    public static B getB() {
        return new B();
    }
    public B getBB() {
        return new B();
    }
}

重名的话处理与上文差不多,但是没有this,因为是static

枚举类

有确定个数个值,只读不需要修改,枚举是一组常量的类,有限的特定的对象。

自定义枚举:

构造器私有化,防止new;去掉set方法,防止修改;在类内部直接创建固定的对象(public,static)

(优化:加个final,防止类加载)

enum关键字:

在这里插入图片描述

valueOf的实际意义:规范用户输入;compareTo:返回编号的相减的值。

package com.enum_;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        System.out.println(Season.SUMMER);
    }
}

enum Season  {
    //用逗号隔开,且常量对象写到最前面
    //使用无参构造器,小括号可以省略!
    TEST,
    TEST2(),
    SPRING("spring","warm"),
    SUMMER("summer","hot"),
    AUTUMN("autumn","sats"),
    WINTER("winter","cold");

    private String name;
    private String desc;

    Season(){}
    Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

不能再继承,但是可以实现接口。

注解

三个基本注解:Override,Deprecated,SupressWarnings

Override:限定某个方法是重写父类的方法。编译器检查是否真的重写了父类的方法,如果没有重写,则报错。

关于@interface:不是接口,而是注解类,jdk1.5之后加入的

Deprecated:表示某个程序的元素已经过时了,过时并不代表不能用,即不推荐使用,可以做到版本的兼容和过渡。

SupressWarnings:抑制编译警告,@SupressWarnings({“all”,“unused”})(需要查文档),作用范围与写的位置有关。

元注解

@Target是修饰注解的注解,叫元注解。(看代码的时候知道在干嘛就可以)

Retention:指定注解的作用范围,SOURCE,CLASS,RUNTIME

Target:指定注解可以在哪些地方使用

Documented:指定该注解是否会在javadoc体现

Inherited:子类会继承父类的注解。

异常

try-catch快捷键:ctrl+alt+t,再选择try-catch

即使出现异常,可以继续执行。

异常分为两大类:error(JVM无法解决的严重问题,严重错误,程序会崩溃),exception(由于编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理,分为运行时异常和编译时异常)

常见的运行时异常:

NullPointerException:空指针异常,对象没有被创建就被使用

ArithmeticException:数学运算异常

ArrayIndexOutOfBoundsException:数组下标越界异常

ClassCastException:类型转换异常,向上转型和向下转型的时候抛出

NumberFormatException:数字格式不正确异常,这种异常可以确保输入的是数字

常见的编译异常:

SQLException//操作数据库时,查询表可能发生异常

IOException//操作文件时,,发生的异常
FileNotFoundException//当操作一个不存在的文件时,发生异常

ClassNotFoundException1/加载类,而该类不存在时,异常

EOFException//操作文件,到文件末尾,发生异常

illegalArguementException//参数异常

异常体系图

在这里插入图片描述
(要会在idea中画这种类图)

异常处理

两种方式:try-catch-finally(程序员在代码中捕获发生的异常,自行处理),throws(异常抛出,交给调用者处理,最顶级的处理者是JVM)

finally:不管try是否有异常,finally始终要执行,通常将释放资源的代码放到这。

throws:被调用的方法发生异常之后不处理异常,直接抛给调用它的方法,再由上一个方法决定怎么处理,如果都不处理,最后给JVM处理。

JVM处理异常:输出异常信息,退出程序。没有显式定义异常捕获,默认就是throw。

try-catch

一旦出现异常,直接进入catch代码块,不继续执行try内后面的内容。

可以有多个catch语句捕获不同异常,要求父类异常在后,子类异常在前,若发生异常,只会匹配一个catch。

可以使用try-finally,由于没有捕获异常,程序会直接崩掉,执行一段代码,不管是否发生异常,都必须执行某个业务逻辑,finally执行完毕后程序退出。

throws

package com.excp;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        try {
            f();
        } catch (ArithmeticException e) {
            System.out.println("cls tcl");
        }
    }
    //throws抛出的可以是这个异常,也可以是这个异常的父类
    public static void f() throws ArithmeticException {
        int num1 = 10, num2 = 0;
        System.out.println(num1/num2);
    }
}

编译异常,程序必须处理,throws给调用者的时候,调用者也必须要处理。

子类重写父类方法时,对抛出的异常:要么一致,要么是父类抛出异常的子类。

throws和try-catch二选一。

自定义异常

package com.defineexcp;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        int n = 15;
        if(!(n>=1&&n<=10)) {
            throw new DefineException("no!");
        }
    }
}

class DefineException extends RuntimeException {
    public DefineException(String message) {
        super(message);
    }
}

一般情况下,自定义异常继承RuntimeException

throw和throws

在这里插入图片描述

throw后跟异常对象,throws后跟异常类型。

包装类

针对八种基本数据类型定义相应的引用类型,包装类(wrapper)中有一些方法。

在这里插入图片描述

装箱和拆箱:

装箱:基本类型到包装类;拆箱:反之。

包装类的其他细节代码:

package com.wapper_;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        //装箱和拆箱:
        int n = 10;
        //自动装箱,底层使用的是:Integer中的valueOf方法
        Integer N = n;
        //自动拆箱,底层使用的是:Integer中的intValue方法
        int n2 = N;

        //包装类->String
        Integer i = 100;
        String s = i+"";
        String s2 = i.toString();
        String s3 = String.valueOf(i);

        //String->包装类
        String str = "12345";
        Integer i1 = Integer.parseInt(str);
        Integer i2 = new Integer(str);

        //包装类常用方法
        //算了,不写了,自己查文档多写代码吧。

        //-128~127之间是同一地址,其他是new的新的Integer
        Integer N1 = 1;
        Integer N2 = 1;
        System.out.println(N1==N2); //true

        Integer N3 = 128;
        Integer N4 = 128;
        System.out.println(N3==N4); //false
    }
}

String类

使用unicode编码(一个字符,不论字母还是汉字,都占两个字节)。不能被继承。

底层仍然是个char数组(private final char value[])!用于存放字符串内容,是个常量,一旦赋值不能被修改。

(是说的地址不可以修改,值可以修改,类似于指针常量!)这也是final的作用,不允许指向新的内存空间。

可以串行化,可以比较大小。
在这里插入图片描述
字符串特性:

常量相加看池中的内容,变量相加看堆中的内容!

.intern是指向对应池中的地址。

常用方法:

equals //区分大小写,判断内容是否相等
equalslgnoreCase//忽略大小写的判断内容是否相等length/获取字符的个数,字符串的长度
indexOf//获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回-1
lastIndexOf//获取字符在字符串中最后1次出现的索引,索引从0开始,如找不到,返回-1substring 1/截取指定范围的子串
trim//去前后空格
charAt:获取某索引处的字符,注意不能使用Str[index]这种方式.
toUpperCasetoLowerCaseconcat
replace替换字符串中的字符
split 分割字符串,对于某些分割字符,我们需要转义比如等
compareTo //比较两个字符串的大小toCharArray 1/转换成字符数组
format //格式字符串,%s字符串%c字符%d 整型%.2f 浮点型案例,将一个人的信息格式化输出.

StringBuffer类

它的内部实现时,有属性char[] value,不是final修饰的,因此存放在堆中。

StringBuffer是final类。

和String对比:

String保存字符串常量,里面的值不能更改,每次对String的更新实际上是更改地址,效率低

StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用更新地址,效率较高

和String的转换:

package com.string_;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        //String->StringBuffer
        String str = "cls tcl";
        //第一种
        StringBuffer sb1 = new StringBuffer(str);
        //第二种
        StringBuffer sb2 = new StringBuffer();
        sb2.append(str);

        //StringBuffer->String
        //第一种
        String s1 = sb1.toString();
        //第二种
        String s2 = new String(sb1);

        System.out.println(sb1+" "+sb2+" "+s1+" "+s2);
    }
}

常用方法:

1)增append
2)删delete(start,end)
3)改replace(start,end,string) //将start----end间的内容替换掉,不含end
4)查indexOf //查找子串在字符串第1次出现的索引,如果找不到返回-1
5)插insert
6)获取长度length

StringBuilder类

此类提供与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全的)

该类被设计用作StringBuffer的一个简易计算,单线程优先采用StringBuilder,大多数实现中它比StringBuffer快。

除了线程方面,其他与StringBuffer相似。

String,StringBuffer,StringBuilder比较

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

(2)String:不可变字符序列,效率低,但是复用率高。

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

(4)StringBuilder:可变字符序列、效率最高、线程不安全

(5)String使用注意说明:
string s=“a”;//创建了一个字符串
S += “b”;//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个子符串s+“b”(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大重副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>

(6)结论:如果我们对String 做大量修改不要使用String

一些常用的类

Math,System,BigInteger,BigDecimal,Date,Calender,LocalDate

集合

单列集合:单个对象
在这里插入图片描述

ArrayList和LinkedList比较:

如何选择ArrayList和LinkedList:
如果我们改查的操作多,选择ArrayList
如果我们增删的操作多,选择LinkedList
一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList.

package com.set_;

import java.util.*;

/**
 * @author cls1277
 */
public class Hello {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        List list = new ArrayList();

        //Collection接口常用的方法:
        //add,remove,contains,size,isEmpty,clear,addAll,containsAll,removeAll
        list.add(1277); list.add("cls"); list.add(3.14);

        //Collection的遍历方式:
        //第一种:iterator:最开始不是指向第一个,而是第一个的上面,next是先移动再输出
        Iterator it = list.iterator();
        //快捷键:itit+回车
        while(it.hasNext()) {
            System.out.print(it.next()+" ");
        }
        //退出循环,iterator指向最后一个元素,不可再next
        System.out.println();

        //第二种:增强for循环:底层是迭代器,是简化版的迭代器
        //快捷键:I+回车 / iter+回车
        for(Object it1: list) {
            System.out.print(it1+" ");
        }
        System.out.println();

        //List接口:
        //1.List元素添加顺序与取出顺序相同,且可重复
        list.add("cls");
        //2.List元素每个元素都有对应的顺序索引,支持索引
        System.out.println(list.get(0));
        //3.List实现子类很多,常用的是Vector,ArrayList,LinkedList

        //List接口的常用方法:
        //void add(int index, Object ele)在index位置插入ele元素
        //boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
        //Object get(int index):获取指定index位置的元素
        //int indexOf(Object obj):返回obj在集合中首次出现的位置
        //int lastIndexOf(Object obj):返回obj在当前集合中未次出现的位置
        //Object remove(int index):移除指定index位置的元素,并返回此元素
        //Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
        //List subList(int fromIndex, int tolndex):返回从fromlndex到tolndex位置的子集合

        //List的遍历方法:
        //iterator和增强for
        //第三种
        for(int i=0; i<list.size(); i++) {
            System.out.print(list.get(i)+" ");
        }
        System.out.println();

        //ArrayList:
        //空可以放到里面,且多个
        //底层是数组实现的
        //基本等同于Vector,但是ArrayLiat线程不安全,效率高
        list.add(null);
        //扩容机制:
        //ArrayList中维护了一个Object类型的数组elementData : transient Object[] elementData;
        //当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
        //如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。

        //Vector:
        //是线程安全的,需要线程同步时,考虑使用Vector

        //LinkedList:
        //底层实现了双向链表和双向队列的特点
        //可以添加任意元素包括null,线程不安全,没有实现同步
        //LinkedList底层维护了一个双向链表、
        //LinkedList中维护了两个属性first和last分别指向首节点和尾节点
        //每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表.
        //所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。

        System.out.println("list="+list);

        //Set接口常用方法:
        //添加和取出顺序不同;没有索引;不允许重复元素,最多一个null
        //常用方法与Collection一样!
        //遍历方法:与Collection一样!

        Set set = new HashSet();
        set.add("cls"); set.add("tcl");
        set.add(1277); set.add(null);

        for (Object o : set) {
            System.out.print(o+" ");
        }
        System.out.println();

        //HashSet:
        //HashSet底层其实是HashMap,数组+单向链表
        //添加一个元素时,先得到hash值-会转成->索引值
        //找到存储诸数据表table,看这个索引位置是否已经存放的有元素
        //如果没有,直接加入
        //如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后
        //在Java8中,如果一条链表的元素个数到 TREEIFY_THRESHOLD(默认是8),并且table的大小>=
        //MIN TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
        //若不大于等于64,则先把table数组扩容(按两倍扩,直到到64,再树化)

        //LinkedHashSet:
        //LinkedHashSet是HashSet的子类
        //LinkedHashSet底层是一个 LinkedHashMap,底层维护了一个数组+双向链表(不同列之间的链表之间相连)
        //LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。
        //LinkedHashSet不允许添加重复元素

        LinkedHashSet set2 = new LinkedHashSet();
        set2.add("cls"); set2.add("tcl"); set2.add(null); set2.add(1277);
        //输入顺序和输出顺序相同

        for (Object o : set2) {
            System.out.print(o+" ");
        }

    }
}

双列集合:键值对形式
在这里插入图片描述

package com.map_;

import java.util.*;

/**
 * @author cls1277
 */
public class Hello {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        //Map特点:
        //Map用于保存具有映射关系的数据:Key-Value
        //Map中的key 和 value可以是任何引用类型的数据,会封装到HashMap$Node对象(静态内部类)中
        //为了方便程序员遍历,创建了entrySet集合,数据类型为Entry,每一个Entry有key和value,其实存放的是HashMap$Node的地址
        //因为HashMap$Node实现了Entry<K,V>的接口
        //Map 中的key不允许重复,原因和HashSet一样,重复了就要替换掉。
        //Map 中的value可以重复
        //Map 的key可以为null, value也可以为null,注意key为null, 只能有一个,value为null ,可以多个
        //常用String类作为Map的key(一般来说)
        //key 和 value 之间存在单向一对一关系,即通过指定的key 总能找到对应的value
        //没有实现同步,线程不安全

        //Map常用方法:
        Map map = new HashMap();
        //put,remove,get,size,isEmpty,clear,containsKey,keySet,entrySet,values
        map.put("1","cls");
        map.put("0","tcl");
        map.put("2","1277");
        map.put("100","cls1277");

        //Map遍历方式:
        //第一种
        Set keyset = map.keySet();
        for (Object o : keyset) {
            System.out.println(o+":"+map.get(o));
        }
        System.out.println();

        //第二种
        Iterator it = keyset.iterator();
        while (it.hasNext()) {
            Object next =  it.next();
            System.out.println(next+":"+map.get(next));
        }

        //第三、四、五种
        Collection values = map.values();
        System.out.println();
        //三种遍历Collections方式
        //增强for,迭代器,下标索引

        //第六、七种
        Set set = map.entrySet();
        //增强for
        for (Object o : set) {
            Map.Entry m = (Map.Entry)o;
            System.out.println(m.getKey()+":"+m.getValue());
        }
        //或者可以迭代器

        //HashMap:
        //是Map的实现类,基本与上面代码相同,在HashSet源码中也有

        //Hashtable:
        //存放的元素是键值对:即 K-V
        // hashtable的键和值都不能为null,否则抛出空指针异常
        //hashTable使用方法基本上和HashMap一样
        //hashTable是线程安全的,hashMap是线程不安全的
        //底层:底层是一个数组,初始大小为11,Hashtable$Entry类型,是内部类,它实现Map.Entry
        //threshold临界值:8 = 11*loadFactor(加载因子,0.75),与HashMap类似
        //扩容:×2+1(11扩容为23)

        //Properties:
        //Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
        //他的使用特点和Hashtable类似
        //Properties还可以用于从 xxx.properties文件中,加载数据到Properties类对象并进行读取和修改
        //说明:工作后 xxx.properties文件通常作为配置文件,这个知识点在IO流举例,有兴趣可先看文章
        //https://www.cnblogs.com/xudong-bupt/p/3758136.html
    }
}

如何选择?

在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择,分析如下:
1)先判断存储的类型(一组对象或一组键值对)
2)一组对象: Collection接口
允许重复:List
增删多:LinkedList [底层维护了一个双向链表]
改查多:ArrayList [底层维护Object类型的可变数组]
不允许重复:Set
无序: HashSet[底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)】
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护数组+双向链表
3)一组键值对:Map
键无序: HashMap [底层是:哈希表jdk7:数组+链表,jdk8:数组+链表+红黑树]
键排序:TreeMap
健插入和取出顺序一致:LinkedHashMap
读取文件Properties

Collections工具类

reverse(List):反转 List 中元素的顺序
shuffle(List):对List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定List 集合元素按升序排序
sort(List, Comparator):根据指定的Comparator 产生的顺序对List集合元素进行排序
swap(List, int, int):将指定list集合中的i处元素和j处元素进行交换

package com.collect;

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

/**
 * @author cls1277
 */
public class Hello {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(1); list.add(100); list.add(5); list.add(200);
        Collections.sort(list);
        System.out.println(list);

        ArrayList list2 = new ArrayList();
        list2.add(1); list2.add(100); list2.add(5); list2.add(200);
        Collections.sort(list2, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                int n1 = (Integer)o1, n2 = (Integer)o2;
                return n2-n1;
            }
        });
        System.out.println(list2);
    }
}

Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection, Comparator):根据Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(Collection,Comparator)
int frequency(Collection,Object):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List 对象的所有旧值

泛型

T和E只能是引用类型,不能是基本数据类型;可以传入规定的类型或者其子类

自定义泛型:

package com.fanxing;

import java.util.ArrayList;

/**
 * @author cls1277
 */
public class Hello {
    @SuppressWarnings({"all"})
    public static void main(String[] args) {
        //类型推断
        Test<String> s = new Test<>();
        s.setA("111");
        System.out.println(s.getA());

        Test<String> s2 = new Test<String>();
        s2.setA("222");
        System.out.println(s2.getA());

        //默认为Object类型
        ArrayList<Object> objects = new ArrayList<>();
        Test t = new Test();

        //普通成员可以使用泛型(属性、方法)
        //使用泛型的数组,不能初始化,不能确定类型,不能实例化,也就不能初始化,即没确定之前不能new
        //静态方法中不能使用类的泛型,静态方法可能会提前类加载,但是此时不能确定类型
        //泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
        //如果在创建对象时,没有指定类型,默认为Object
        //泛型的标识符一般为单个大写字母
    }
}

//自定义泛型类
class Test<E> {
    public E a, b;

    public void setA(E a) {
        this.a = a;
    }

    public E getA() {
        return a;
    }
}

//自定义泛型接口
interface IA<E, T> {
    //接口中,静态成员也不能使用泛型(这个和泛型类规定一样)
    //泛型接口的类型,在继承接口或者实现接口时确定
    //没有指定类型,默认为Object
    public void hi (E e);
    public E ok(E e, T t);
}

//继承接口确定类型
interface IB extends IA<String, Integer> {}

class CB implements IB {
    //自动填充IB确定的类型
    @Override
    public void hi(String s) {

    }

    @Override
    public String ok(String s, Integer integer) {
        return null;
    }
}

//实现接口确定类型
class CC implements IA<String, Integer> {
    //自动确定实现接口时确定的类型
    @Override
    public void hi(String s) {

    }

    @Override
    public String ok(String s, Integer integer) {
        return null;
    }
}

//泛型方法,可以定义在普通类中,也可以定义作泛型类中
//当泛型方法被调用时,类型会确定
//public void eat(E e)0{},修饰符后没有<T,R..> eat方法不是泛型方法,而是使用了泛型
class CD {
    public int n = 10;
    public<E> E geta(E a) {
        return a;
    }
}

class CE<E> {
    public E e;
    //是泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
    public <E> E getEE(E e) {
        return e;
    }
    //不是泛型方法,而是方法使用了泛型
    public E getE(E e) {
        return e;
    }
}

泛型的继承和通配符:

package com.fanxingdetail;

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

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        //泛型不具备继承性
//        List<Object> list = new ArrayList<String>();//对吗?不对!
        //<?>:支持任意泛型类型
        //<? extends A>:支持A类以及A类的子类,规定了泛型的上限
        //<? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
        ArrayList<CA> cas = new ArrayList<>();
        ArrayList<CB> cbs = new ArrayList<>();
        ArrayList<CC> ccs = new ArrayList<>();
        print1(cas); print1(cbs); print1(ccs);//无报错
        print2(cas); print2(cbs); print2(ccs);//第一个报错
        print3(cas); print3(cbs); print3(ccs);//第三个报错
    }

    public static void print1(List<?> l) {
        for (Object o : l) {
            System.out.println(o);
        }
    }

    public static void print2(List<? extends CB> l) {
        for (CB cb : l) {
            System.out.println(cb);
        }
    }

    public static void print3(List<? super CB> l) {
        for (Object o : l) {
            System.out.println(o);
        }
    }
}

class CA {
}

class CB extends CA {
}

class CC extends CB {
}

Junit单元测试类

测试框架,是java的单元测试框架

package com.junit_;

import org.junit.jupiter.api.Test;

/**
 * @author cls1277
 */
public class Junit_ {
    public static void main(String[] args) {

    }

    @Test
    public void f1() {
        System.out.println(1);
    }

    @Test
    public void f2() {
        System.out.println(2);
    }
}

线程

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

并行:同一个时刻,多个任务同时执行。多核cpu可以实现并行。

可能并发和并行同时进行。

程序获取当前电脑的cpu个数:

package com.cpu_;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        int cpuNums = runtime.availableProcessors();
        System.out.println(cpuNums);
    }
}

线程的使用方法:

package xiancheng;

/**
 * @author cls1277
 */
public class XianCheng {
    public static void main(String[] args) {
        //线程的使用方法:1.继承Thread类;2.实现Runnable接口
        //使用jconsole:运行程序后,在终端输入jconsole,可以看到线程的运行情况

        //返回主线程的名字:main
        System.out.println(Thread.currentThread().getName());
        //1.继承Thread类
        T1 t1 = new T1();
        //启动线程->最终会执行run方法

        //Q:这里为什么是start而不是run?
        //A:直接调用run,相当于main线程调用的子线程,并没有真的开线程,T1线程类输出的线程名为main
        //如果直接调用run,则必须等run结束后再继续main线程的执行
        //本质上就相当于run方法了
        t1.start();
        //当main线程启动一个子线程后Thread-0后,主线程不会阻塞,会继续执行
        //main线程挂了之后,不会影响子线程的运行
        for(int i=0; i<80; i++) {
            System.out.println("i="+i);
            //主线程休眠1s,与T1线程交替执行
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        //2.实现Runnable接口
        //因为Java是单继承的,所以继承Thread类在一些已经继承过的不能用了,所以只能实现接口
        T2 t2 = new T2();
        //Runnable接口中没有start方法,创建Thread对象,然后再调用start方法
        //为什么可以把t2放给Thread对象呢?使用了设计模式:静态代理模式。
        //t2实现了Runnable,Thread的构造器中是Runnable类型的,所以t2可以直接放到Thread的构造器中
        Thread thread = new Thread(t2);
        thread.start();
    }
}

//当一个类继承了Thread类,该类就可以当作一个线程
class T1 extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
        int cnt = 0;
        while(true) {
            System.out.println("cls tcl!");
            cnt++;
            if(cnt==80) {
                //当函数退出,该线程也就退出了
//                break;
                return;
            }
            //让该线程休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class T2 implements Runnable {
    @Override
    public void run() {
        int cnt = 0;
        System.out.println(Thread.currentThread().getName());
        while(true) {
            System.out.println("cls  tcl!");
            cnt++;
            if(cnt==5) {
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程终止:(通知的方式)

package com.stoppro_;

/**
 * @author cls1277
 */
public class StopPro {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        Thread.sleep(5000);
        t.setLoop(false);
    }
}

class T extends Thread {
    private boolean loop = true;
    private int cnt = 0;

    //main线程通知T线程终止
    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while(loop) {
            cnt++;
            System.out.println(cnt+"cls tcl!");
            if(cnt==100) {
                return ;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程常用方法:

setName//设置线程名称,使之与参数name 相同
getName//返回该线程的名称
start//使该线程开始执行;Java虚拟机底层调用该线程的 start0方法
run//调用线程对象 run方法;
setPriority //更改线程的优先级
getPriority//获取线程的优先级
sleep//在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
interrupt //中断线程,不是结束线程,一般用于中断正在休眠的线程

线程优先级范围:MAX_PRIORITY:10,MIN_PRIORITY:1,NORM_PRIORITY:5

如果线程被interrupt了,则进入catch的代码块中!

yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功(cpu可能觉得自己可以,就礼让不成功)
join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

用户线程和守护线程:

用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。常见的守护线程:垃圾回收机制

package com.promethod;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        //注意顺序!!!
        t.setDaemon(true);
        t.start();
        for(int i=0; i<5; i++) {
            System.out.println("n tql!");
            Thread.sleep(1000);
        }
    }
}

class T extends Thread {
    @Override
    public void run() {
        while(true) {
            try {
                Thread.sleep(1000);
                System.out.println("cls tcl!");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程的生命周期:(getState获取进程当前状态;Thread.State是个枚举类,包含线程可以存在的状态,用于判断)
在这里插入图片描述
线程同步机制:

在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
也可以这里理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

package com.tongbu;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        Sell sell = new Sell();
//        new Thread(sell).start();
//        new Thread(sell).start();
//        new Thread(sell).start();
        Thread thread = new Thread(sell);
        Thread thread1 = new Thread(sell);
        Thread thread2 = new Thread(sell);
        thread.setName("1");
        thread1.setName("2");
        thread2.setName("3");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

class Sell implements Runnable {

    private int ticketNum = 100;

    public synchronized void f() {
        if(ticketNum<=0) {
            System.out.println("over!");
            return ;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+" sell!");
        ticketNum--;
    }

    @Override
    public void run() {
        while (true) {
            f();
            if(ticketNum<=0) break;
        }
    }
}

互斥锁

package com.tongbu;

/**
 * @author cls1277
 */
public class Hello {
    public static void main(String[] args) {
        Sell sell = new Sell();
//        new Thread(sell).start();
//        new Thread(sell).start();
//        new Thread(sell).start();
        Thread thread = new Thread(sell);
        Thread thread1 = new Thread(sell);
        Thread thread2 = new Thread(sell);
        thread.setName("1");
        thread1.setName("2");
        thread2.setName("3");
        thread.start();
        thread1.start();
        thread2.start();
    }
}

class Sell implements Runnable {

    private int ticketNum = 100;

    //方法:锁在了this上
    public synchronized void f() {
        if(ticketNum<=0) {
            System.out.println("over!");
            return ;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+" sell!");
        ticketNum--;
    }

    //代码块:锁在了synchronized中的括号中
    public void m() {
        synchronized (this) {
            if(ticketNum<=0) {
                System.out.println("over!");
                return ;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+" sell!"+" "+ticketNum);
            ticketNum--;
        }
    }

    //锁不在this上,因为没有this,加在了Sell.class上
    public synchronized static void n() {

    }

    //上面等价于
    public static void n2() {
        synchronized (Sell.class) {
            
        }
    }

    @Override
    public void run() {
        while (true) {
//            f();
            m();
            if(ticketNum<=0) break;
        }
    }
}

注意事项和细节:
同步方法如果没有使用static修饰:默认锁对象为this
如果方法使用static修饰,默认锁对象:当前类.class
实现的落地步骤:
需要先分析上锁的代码选择同步代码块或同步方法
要求多个线程的锁对象为同一个!

IO流

创建文件:

package com.file_;

import org.junit.jupiter.api.Test;

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

/**
 * @author cls1277
 */
public class file_ {
    public static void main(String[] args) {

    }

    //方式1
    @Test
    public void c1() {
        String path = "D:\\cls1277\\Java\\IDEA\\new1.txt";
        File file = new File(path);
        try {
            file.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //方式2
    @Test
    public void c2() {
        File file = new File("D:\\cls1277\\Java\\IDEA\\");
        File file1 = new File(file, "new2.txt");
        try {
            file1.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //方式3
    @Test
    public void c3() {
        File file = new File("D:\\cls1277\\Java\\IDEA\\", "new3.txt");
        try {
            file.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

获取文件信息:

getName,getAbsolutePath,getParent,length,exists,isFile,isDirectory

目录操作:

delete:删除空目录或文件;mkdir:创建目录,多级目录用mkdirs,无参。

在这里插入图片描述

InputStream、OutputStream、Reader、Writer都是抽象类

io流:https://blog.csdn.net/qq_44715943/article/details/116501936

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cls1277

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值