Java基础

文章目录


化繁为简,先死后活

当写分支语句时,判断条件是否错误 (业务错误而非逻辑错误) 比判断条件是否正确的扩展性和可读性更好。过关斩将才能执行业务逻辑代码。

IDEA快捷键

删除当前行(delete):Ctrl + D

复制并粘贴当前行(duplicate):Ctrl + Alt + 向下光标

补全代码:Alt + /

注释:Ctrl + /

快速格式化代码(Reformat):Ctrl + Alt + L

快速运行程序(run):Alt + R

自动生成构造器:Alt+Insert(F12)快捷键,选择Constructor

查看类的继承关系:Ctrl + H

定位到方法:将光标放在一个方法上,输入Ctrl + B,可以定位到方法,Ctrl + 点击鼠标左键 也可以

自动分配变量名:对象后面加 .var

模板快捷键(例如sout等):file -> settings -> editor -> Live templates ->

显式所有快速模板的快捷键:Ctrl + J

断点调试(debug)

介绍

  • 在断点调试过程中,是运行状态,是以对象的运行类型来执行的
  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个bug
  • 在debug过程中,也可以动态得下断点

快捷键

F7:跳入方法内

F8:逐行执行代码

Shift + F8:跳出方法

F9:resume,执行到下一个断点(可以跨方法)

Java的编译执行

编译型:将源代码文件编译为中间代码或机器代码文件;

解释型:边编译边执行。

Java的源代码文件先被编译器编译为字节码文件,然后在JVM里解释执行。

javap:反编译。javap Xxxx.class。

转义字符

\t 制表符

  • 前面的输出内容位数为8的倍数,\t将输出8个空格
  • 前面的输出内容位数不是8的倍数,\t将补足8位

\n 换行符

\\ 代表\

\" 一个"

\r 一个回车 System.out.println("子曰学而时\r习之");

​ 在Java在线工具上输出

子曰学而时

习之

​ 在IDEA里输出

习之

​ 在命令行里输出

习之学而时

路径

相对路径:从当前目录开始定位,形成的一个路径

绝对路径:从根目录开始定位,形成的一个路径

Java的数据类型及转换

数据类型

Java是强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义才能使用。

位是计算机最小存储单位。字节是计算机数据处理最小单位。

基本数据类型

boolean:占1字节,只有true和false两个值(单独使用时占4个字节,成数组使用时每项占1个字节)

byte:占1字节,表示范围为-128~127

short:占2字节,表示范围为-32768~32767

int:占4字节,表示范围为-2147483648~2147483647

long:占8字节,表示范围为-9223372036854775808~9223372036854775807

float:占4字节,表示范围为

double:占8字节,浮点数默认类型为double

char:占2字节,char的本质是一个整数,默认输出时为unicode码对应的字符

long数据类型的值要在数字后面加L,例如:long num = 30L;

float数据类型的值要在数字后面加F,例如:float num = 50.1F;

浮点数运算不精确,例如double d1=1.0;double d2=0.9;Syetem.out.println(d1-d2);,结果为0.09999999999999998

对小数运算结果进行相等判断时要小心,应该以两个数的差值的绝对值在某个精度范围内来判断。

PS:二进制数0b… 八进制数0… 十六进制数0x…

数据类型转换

基本数据类型转换

运算时,不同类型的数据先转换为同一类型,然后进行运算

优先级:byte,short,char < int < long < float < double

高优先级类型的数据转换为低优先级类型的数据时需要强制类型转换,容易造成精度丢失或溢出;

低优先级类型的数据转换为高优先级类型的数据时为隐式转换即自动转换。

byte,short,char计算时,转换为int类型。(包括两个byte类型的数据的计算)

public class Test0225_01 {
    public static String getType(Object o){
        return o.getClass().toString(); //使用int类型的getClass()方法
    }

    public static void main(String[] args) {
        float num1 = 9.13F;
        double num2 = 9.13;
        System.out.println((int)num1);   //精度丢失
        System.out.println((int)num2);
        System.out.println("===========================================");
        char letter1 = 'b';
        int num3 = letter1+1;    //b的Unicode编码为98
        System.out.println(num3);
        System.out.println((char)num3);   //强制类型转换,int型转换为char型,输出该数字所对应的Unicode字符
        System.out.println("===========================================");
        int num4 = 128;
        byte num5 = (byte)num4;    //内存溢出,byte表示范围为-128~127
        double num6 = num4;       //隐式类型转换
        System.out.println("num4:"+getType(num4));
        System.out.println(num5+"\n"+num6);
        System.out.println("===========================================");
        int num7 = 10_0000_0000;     //jdk7新特性:数字之间可以用下划线分割
        int num8 = 20;
        int num9 = num7*num8;           //内存溢出
        long num10 = num7*num8;         //类型转换发生在运算之后,转换成功,但运算结果已经是溢出后的错误结果
        long num11 = num7*((long)num8); //类型转换发生在运算之前,num8转换为long型,int型与long型运算,自动转换为long型
        long num12 = num7*num8;
        System.out.println("int:"+num9);
        System.out.println("long:"+num10);
        System.out.println("long:"+num11);
        System.out.println("long:"+num12);
        System.out.println("num7:"+getType(num7));
        System.out.println("num8:"+getType(num8));
        /*
        经过以上测试,低优先级类型数据在与高优先级类型数据运算时,低优先级类型变量的值隐式转换为高优先级,
        但该变量仍为低优先级,且这种转换只暂时存在运算过程中,运算结束后,值仍为低优先级类型。
         */
    }
}

引用数据类型转换

引用数据类型的强制类型转换

public class Apply {
    public static void main(String[] args) {
        Student s1 = new Student();
        People s2 = new Student();
        People s3 = new People();
        s1.say();
        s2.say();
        s3.say();
        s1.hello();
        s2.hello();
        s3.hello();
        s1.bye();
        Student s = (Student)s2;
        /*
        s2所引用的对象是Student类型,但s2的引用数据类型是People,不能调用Student的独有方法,
        把引用变量s2强制转化为Student类型,这时,Student类型的引用变量引用Student类型的对象,
        可以调用Student类的独有方法。
        */
        s.bye();
        //s3.bye;
    }
}


public class Student extends People{
    public void say(){
        System.out.println("Student");
    }
    public void bye(){
        System.out.println("bye");
    }
}

public class People {
    public void say(){
        System.out.println("People");
    }
    public void hello(){
        System.out.println("hello");
    }
}

Java的运算符

算数运算符

10.0 / 4 ==2.5;

%取模的本质是:a % b == a - a / b * b; 故 -10 % 3 == -1 ; 10 % -3 == 1;

上式中当a是小数时,a % b == a - (int)a / b * b ; 故-10.5 % 3 = -1.5;有小数运算,结果是近似值

位运算

位运算符:& | ~ ^ << >> >>> ,补码参与运算

  • &:按位与,双目运算符

  • |:按位或,双目运算符

  • ~:按位取反,单目运算符

  • ^:异或,双目运算符(异为1,同为0)

  • <<:算术左移,代表×2,符号位不变,低位补0,

    双目运算符(2<<3=16,2=00000000 00000000 00000000 00000010,16=… 0001 0000,2中的1左移三位)

  • >>:算术右移,代表÷2(取整),双目运算符,低位溢出,符号位不变,符号位补溢出的高位

  • >>>:无符号右移,低位溢出,高位补0

自增自减运算

自增自减运算符:++ –

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

int i = 1;
i = i++;
System.out.println(i);//1
/*
在i = i++中,i先将值赋给缓存变量temp,然后i自增,然后temp的值赋给i。
若把i++改为++i,则i先自增,再把值赋给缓存变量temp,然后temp的值赋给i。
*/
int i = 1;
int j = 0;
j = i++;
System.out.println(i);//2
System.out.println(j);//1
/*
在j = i++中,i先将值赋给缓存变量temp,然后i自增,然后temp的值赋给j。
*/
public class Test0226_01 {
    public static void main(String[] args) {
        int a = 5;
        boolean b = a++<=5;    //a++,a先进行逻辑运算,然后再自增
        System.out.println(a);
        System.out.println(b);
        boolean c = ++a<=6;    //++a,先自增,然后再进行逻辑运算
        System.out.println(a);
        System.out.println(c);
    }
}

逻辑运算符

短路与&& 短路或|| 取反(非运算)!

逻辑与& 逻辑或| 逻辑异或^

赋值运算符

+=,-=,*=,/=等复合赋值运算符会进行类型转换

byte b = 3;
b = b + 2;//会报错,类型不兼容 ,b+2的运算结果是int类型,赋给byte类型的变量会报错
b += 2;//不会报错,等价于b = (byte)(b+2)

三元运算符

条件表达式?表达式1:表达式2

如果条件表达式为true,则运算结果是表达式1;否则为表达式2。

要看作一个整体,低优先级类型的数据会自动转换为高优先级类型的数据。

比较运算符

== 是一个比较运算符。既可以判断基本数据类型,也可以判断引用数据类型。前者判断值是否相等,后者判断地址是否相等即是不是同一个对象。

原码 反码 补码

1.二进制的最高位是符号位:0表示整数,1表示负数

2.正数的原码,反码,补码都一样(三码合一)

3.负数的反码=原码符号位不变,其他位取反

4.负数的补码=反码+1 , 负数的反码=补码-1

5.0的反码,补码都是0

6.计算机没有无符号数

7.在计算机运算的时候,都是以补码的方式来运算的

8.当我们看运算结果的时候,要看它的原码

进制转换

十进制小数转换成二进制:利用小数部分乘2,取整数部分,剩下的小数部分继续乘2,直至小数点后为0

JavaDoc

/**
*这是一个javadoc示例
*@author gzy
*@version 1.0
*@param 参数
*@return 返回值类型
*/

生成对应的文档注释 javadoc -d 文档存放路径 -xx -yy test.java

类,方法的注释要以javadoc的方式来写

非javadoc的注释,往往是给代码的维护者看的

Ctrl+/:选中行被注释

IDEA导出JavaDoc:Tools->Generate JavaDoc->Other command line arguments:-encoding utf-8 -charset utf-8

Scanner类

public class Test0226_02 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输出第一个字符串");
        //输入“测试 程序”,scanner将空格判定为分隔符,将字符串判断为两次数据输入,str2不需手动输入,scanner自动将“程序”分配给str2
       // System.out.println(scanner.hasNext());
        String str1 = scanner.next();
        System.out.println(scanner.hasNext());
        System.out.println("str1"+str1);
        /*
        请输出第一个字符串
        测试
        程序
        true
        str1测试
        请输出第二个字符串
        str2程序
        请输出第三个字符串
         */

        System.out.println("请输出第二个字符串");
        String str2 = scanner.next();
        System.out.println("str2"+str2);

        System.out.println("请输出第三个字符串");
        if(scanner.hasNext()){
            String str3 = scanner.next();
            System.out.println(str3);
        }
        System.out.println(scanner.hasNext());
        String str4 = scanner.next();
        System.out.println(str4);
        scanner.close();
    }
}
public class Test0227_01 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入一个字符串");
        //当调用Scanner类的方法时,就是键盘输入的时候,而不必等待Next方法和接收变量
        System.out.println(scanner.hasNext());
        scanner.close();
    }
}
Scanner myScanner = new Scanner(System.in);
char c = myScanner.next().charAt(0);//获取输入字符

switch语句

switch-case语句具有穿透性,如果执行case语句后没有break语句,那么程序会执行下一个case语句并且不会判断是否符合该case常量。

表达式数据类型应和case后的常量数据类型一致,或者是可以自动转成可以相互比较的类型,比如输入的是char,而常量是int。

switch(表达式)中表达式的返回值必须是:(byte,short,char,int,String,enum)

switch(month){
    case 3:
    case 4:
    case 5:
		System.out.println("这是春季");//穿透使代码更简洁
        break;
}

循环

break:退出循环

continue:退出本次循环,继续下一轮循环

增强for循环:foreach

for (数据类型  变量名: 该数据类型的数组或集合){//每轮把数组或集合的一个元素赋给变量名
    //对变量名进行操作
}

数组

初始化

静态初始化

int[] a = {1,5,6};
Man[] mans = {new Man[],new Man[]};

动态初始化

int[] a = new int[2];//数组长度可以是变量,在运行中再确定
a[0] = 1;
a[1] = 5;

默认初始化

数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

数组赋值机制

引用传递

在这里插入图片描述

二维数组

二维数组的元素数组的长度可以不同

public class Test0303_01 {
    public static void main(String[] args) {
        int[][] arrays = {{1,2},{2,3,4},{3,4,5,8}};//静态初始化
        for (int[] arr:arrays){
            for (int a:arr){
                System.out.print(a+"\t");
            }
            System.out.println();
        }

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

        int[][] arrays2 = new int[2][]; //动态初始化,二维数组大小不可不初始化
        arrays2[0] = new int[2];
        arrays2[1] = new int[3];
        for (int i = 0; i < arrays2.length; i++) {
            for (int j = 0; j < arrays2[i].length; j++){
                System.out.print(arrays2[i][j]);
            }
            System.out.println();
        }
    }
}

稀疏数组

当一个数组中大部分元素为0,或者为同一值的数组时,可以使用稀疏数组来保存该数组。

原始数组转换为稀疏数组:

  • 把原始数组的行数,列数,不同值元素的个数等记录在稀疏数组的第0行
  • 把不同值元素的所在行号,所在列号,值等记录在稀疏数组的其他行

排序

冒泡排序

两层循环嵌套,外层控制轮次(一般待排序数为多少就设置多少轮),内层依次使相邻两数比较。

内层从前向后依次比较:若前数>后数,则使之互相交换,反之不作为,则排序完毕为升序,向数组尾部冒泡;

​ 若前数<后数,则使之互相交换,反之不作为,则排序完毕为降序,向数组尾部冒泡;

内层从后向前依次比较:若前数>后数,则使之互相交换,反之不作为,则排序完毕为升序,向数组首部冒泡;

​ 若前数<后数,则使之互相交换,反之不作为,则排序完毕为降序,向数组首部冒泡。

类与对象

**类的五大成员:**属性;方法;构造器;代码块;内部类

类中的成员变量默认初始化值:整数:0 浮点数:0.0 char:u0000 boolean:false 引用:null

对象在内存的存在形式

在这里插入图片描述

对象创建流程

1.加载类信息,只会加载一次

2.在堆中分配空间(地址)

3.完成对象初始化

​ 3.1默认初始化

​ 3.2显式初始化

​ 3.3构造器初始化

4.将对象在堆中的地址返回给引用变量

关于创建对象时,类中的属性和方法的调用和访问顺序,详情见章节《代码块》——细节。

方法

调用机制内存分析

  • 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
  • 当一个方法执行完毕,或者执行到return语句时,就会返回
  • 当一个方法执行完毕,该方法栈废弃被回收
  • 返回到调用方法的地方
  • 返回后继续执行方法后面的代码
  • 当主方法(栈)执行完毕后,整个程序退出

main方法

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

  • main方法是虚拟机调用的

  • JVM需要调用类的main()方法,所以该方法的访问权限必须是public

  • JVM在执行main()方法时不必创建对象,所以该方法必须是静态的static

  • 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行类的参数

  • java 执行的程序 参数1 参数2 参数3 … (详情见命令行传参)

特别提示:

1.在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。

2.但是,不能直接访问该类的非静态成员,必须创建该类的一个实例化对象后,才能通过这个对象去访问类中的非静态成员。

命令行传参

在命令行参数中,各个参数之间以空格分隔,但在某些情况下,这种处理方式并不合适。比如需要程序处理一个在某目录下的文件,但是该目录中间有空格,这就会导致程序无法得到正确的文件路径。

例如,在命令行输入:java getLinePara c:\My Document\test.java

相应的输出为: 第0个参数是:c:\My 第1个参数是:Document\test.java它把一个参数拆成两个来处理,显然不符合程序员的设想。在这种情况下,用户需要将命令行参数用双引号括起来。

上面的命令应该写成这个样子:java getLinePara “c:\My Document\test.java”

相应的输出结果为: 第0个参数是:c:\My Document\test.java

在IDEA中想给main方法传入参数的方法是:点击右上角"Edit Configurations…",选择类,在"Program arguments"栏中输入参数即可

可变参数

在方法声明中,在指定参数类型后加一个省略号(…)。

一个方法的形参列表种最多有一个可变参数,它必须是方法的最后一个参数,任何的普通参数必须在它之前声明。

可变参数相当于一个可变长度数组变量,待调用方法传入实际参数时,长度才确定。可以通过下标遍历。

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

public class Test(){
    public static int intLength(int... a){
        int length = 0;
        for(int b:a){
            length++;
        }
        return length;
    }
    public static void main(String[] args){
        int length = intlength(5,88,99,74,6,2,26,9,6,5);
        System.out.println(length);
    }
}

静态方法

静态方法的方法体里不能调用非静态方法,静态方法随类一起加载,非静态方法随对象一起加载,前者时间片更早出现。

构造方法

作用:不是创建对象,而是初始化对象

构造方法没有返回值。

方法名字和类名字必须一样。

构造器的调用,由系统自动完成。

当一个类中不显示定义构造方法时,Java自动隐式定义一个默认无访问修饰符无方法体无参数的构造方法。当类中自定义一个构造方法时,隐式无参数构造方法被覆盖。若想要再次使用无参构造器,此时需显式定义无参数构造方法(为了子类的构造器构造方便)。

Alt+Insert(F12)快捷键:选择Constructor,IDEA自动生成构造器。

引用类型参数传递

如果将Java引用类型变量作为参数传递给方法,是将引用变量的值传递给形参,而引用变量的值实际上就是引用对象在堆内存中的地址。也就是说,这个时候实参和形参指向了同一个对象,如果利用形参进行操作,操作的就是实参指向的对象,最后通过实参的那个引用访问,自然是被形参操作过的结果。

这也是多态的作用原理之一

public class ArrayTest {
	public static void main(String[] args) {
		int[] arr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
		System.out.print("Before calling the inversion: ");
		for (int i : arr) {
			System.out.printf("%3d", i);
		}
		System.out.println();
		inversion(arr);
		System.out.print(" After calling the inversion: ");
		for (int i : arr) {
			System.out.printf("%3d", i);
		}
		System.out.println();
		
		setNull(arr);
		System.out.println("@" + arr);
	}
	public static void inversion(int[] arr) {
		int length = arr.length;
		for (int i = 0; i <= length / 2; i++) {
			int temp = arr[i];
			arr[i] = arr[length - i - 1];
			arr[length - i -1] = temp;
		}
	}
	
	public static void setNull(int[] array) {
		System.out.println("#" + array);
		array = null;
		System.out.println("$" + array);
	}
}

程序运行结果:

Before calling the inversion:   0  1  2  3  4  5  6  7  8  9
After calling the inversion:   9  8  7  6  4  5  3  2  1  0
#[I@1d251891
$null
@[I@1d251891 

递归

递归必须向退出递归的条件逼近,否则就是无限递归。

在这里插入图片描述

  • 老鼠迷宫问题(递归回溯)
/*
迷宫问题,八行八列。
0表示无障碍物,1表示障碍物,2表示通路,3表示无障碍物但路不通。
第一行,最后一行,第一列,最后一列是围墙,第四行第二列第三列是围墙。
即map[0][]=1,map[7][]=1,map[][0]=1,map[][7]=1,map[3][1]=1,map[3][2]=1。
起点:map[1][1],终点:map[6][6]。
寻路顺序为下右上左。
 */
public class test {
    public static void main(String[] args) {
        Tools tools = new Tools();
        int[][] map = new int[8][8];
        for (int i = 0; i < map.length; i++) {
            map[i][0] = 1;
            map[i][map[0].length-1] = 1;
        }
        for (int i = 0; i < map[0].length; i++) {
            map[0][i] = 1;
            map[map.length-1][i] = 1;
        }
        map[3][1] = 1;
        map[3][2] = 1;

        boolean res = tools.findWay(map,1,1);
        if (res){
            for (int i = 0; i < map.length; i++) {
                for (int j = 0; j < map[0].length; j++) {
                    System.out.print(map[i][j]+" ");
                }
                System.out.println();
            }
        }
    }
}

class Tools {
    //i,j表示现在老鼠所处位置
    public boolean findWay(int[][] map,int i,int j){
        if (map[6][6] == 2){
            return true;//结束递归
        }else{
            if (map[i][j] == 0){
                map[i][j] = 2;//假设为通路
                /*
                开始通过下右上左的顺序判断临近位置是否为通路,临近位置进入下一层递归,它也不能立即返回布尔值,而是一直下钻直到					终点为2或进入死路开始回溯,才开始return布尔值。
                */
                if (findWay(map, i + 1, j)){//下
                    return true;
                }else if (findWay(map, i, j + 1)){//右
                    return true;
                }else if (findWay(map, i - 1, j)){//上
                    return true;
                }else if (findWay(map, i, j - 1)){//左
                    return true;
                }else{//该位置的上下左右均无未走过的通路,即该位置能走但走不通
                    map[i][j] = 3;//上述假设不成立
                    return false;
                }
            }else{//map[i][j]为1,2,3
                return false;
            }
        }
    }
}
  • 汉诺塔问题
/*
汉诺塔问题
A,B,C三根柱子,将A上的n个盘子移到C上,问移动步骤和步数?要求移动过程中始终保持大盘在下,小盘在上。
若n=1,将一号盘子从A上移到C上,完成。1步
若n=2,将一号盘子从A上移到B上,将二号盘子从A上移到C上,将一号盘子从B上移到C上,完成。3步
若n=3,将一号盘子从A上移到C上,将二号盘子从A上移到B上,将一号盘子从C上移到B上,将三号盘子从A上移到C上,将一号盘子从B上移到A上,将二号盘子从B上移到C上,
将一号盘子从A上移到C上,完成。7步
若n=n,则步数为2^n-1,步骤为:先将1~n-1号盘子从A移到B上(C为辅助);再将n号盘子从A移到C上;最后将1~n-1号盘子从B移到C上(A为辅助)
 */
public class test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        char start = 'A';
        char helper = 'B';
        char end = 'C';
        System.out.println("请输入盘子的数量:");
        int diskNum = scanner.nextInt();
        int res = Hanoi.hanoi(diskNum, start, helper, end);
        System.out.println("总步数为"+res);
    }
}

class Hanoi{
    static int stepNum = 0;
    public static void mov(int disk, char start, char end){//A为起点柱,C为终点柱
        System.out.println("第"+(++stepNum)+"步:将第"+disk+"号盘子从"+start+"上移到"+end+"上");
    }
    public static int hanoi(int disksNum, char start, char helper, char end){
        if (disksNum == 1){
            mov(disksNum, start,end);//当要挪动的盘子为1个时,结束递归,方法栈从栈顶自上而下开始出栈即方法结束
        }else{
            hanoi(disksNum-1, start, end, helper);
            mov(disksNum, start, end);
            hanoi(disksNum-1, helper, start, end);
        }
        return stepNum;
    }
}
/*
请输入盘子的数量:
4
第1步:将第1号盘子从A上移到B上
第2步:将第2号盘子从A上移到C上
第3步:将第1号盘子从B上移到C上
第4步:将第3号盘子从A上移到B上
第5步:将第1号盘子从C上移到A上
第6步:将第2号盘子从C上移到B上
第7步:将第1号盘子从A上移到B上
第8步:将第4号盘子从A上移到C上
第9步:将第1号盘子从B上移到C上
第10步:将第2号盘子从B上移到A上
第11步:将第1号盘子从C上移到A上
第12步:将第3号盘子从B上移到C上
第13步:将第1号盘子从A上移到B上
第14步:将第2号盘子从A上移到C上
第15步:将第1号盘子从B上移到C上
总步数为15
*/
  • 八皇后问题
public class test {
    int count = 0;
    int[] arr = new int[8];

    public static void main(String[] args) {
        int row = 0;
        new test().judge(row);
    }

    public void judge(int row){
        if (row == 8){
            count++;
            System.out.println("第"+count+"种");
            for (int j = 0; j < arr.length; j++) {
                System.out.print(arr[j]+" ");
            }
            System.out.println();
            return;
        }

        for (int i = 0; i < arr.length; i++){
            arr[row] = i;
            boolean req = this.panduan(row);
            if (req){
                judge(row+1);
            }
        }
    }

    public boolean panduan(int row){
        for (int i = 0; i < row; i++) {  //判断是否符合要求
            if (arr[i] - arr[row] == 0 || arr[i] - arr[row] == i - row || arr[i] - arr[row] == row - i){
                return false;
            }
        }
        return true;
    }

}

重载

名称相同,形参列表不同

作用域

变量作用域

全局变量(成员变量)可以不赋值直接使用,有默认值;局部变量没有默认值,必须赋值后才能使用。

属性和局部变量可以重名,访问时遵循就近原则。

同一个作用域内,两个局部变量不能重名。

属性生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。局部变量生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而销毁。

包的本质:创建不同的文件夹来保存文件

不同包下的类可以同名

OOP三大特性

封装

​ 封装就是把抽象出的数据**[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法]**,才能对数据进行操作。

封装的好处:

  • 隐藏实现细节
  • 可以对数据或权限进行验证,保证安全合理

封装的实现步骤

  • 将属性进行私有化(本类之外不能直接修改属性值)

  • 提供一个公共的(public)set方法,用于对属性判断并赋值

    public void setXxx(类型 参数名){//Xxx表示某个属性
        //加入数据验证的业务逻辑
        属性 = 参数名
    }
    
  • 提供一个公共的(public)get方法,用于获取属性的值

    public Xxx的数据类型 getXxx(){//权限判断
        return Xxx;
    }
    

构造器与setter结合完善防护机制:

public 构造方法名(数据类型 xxx, 数据类型 xyy){
    setXxx(xxx);
    setYyy(yyy);
}

Alt+Insert(F12)快捷键:选择Getter或Setter或Getter and Setter,IDEA自动生成访问器get()或更改器set()

继承

格式:

class 子类 extends 父类{
}
//子类自动拥有父类定义的属性和方法
//父类又叫超类,基类
//子类又叫派生类

好处:

  • 提高代码的复用性
  • 提高代码的扩展性和维护性

细节:

  • 子类继承了所有的属性和方法,但是在同一个包内,私有属性和方法不能在子类直接访问,要通过父类的公共方法去访问或调用
  • 子类必须调用父类的构造器,完成子类的初始化
  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定调用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
  • 如果希望指定去调用父类的某个构造器,则显式得调用一下:super(实际参数列表)
  • super在使用时,必须放在构造器得第一行
  • super() 和 this() 都只能放在构造器第一行,因此这两个方法不能在同一个构造器中共存(super隐式也不能共存)
  • java所有类都是Object类的子类
  • 父类构造器的调用不限于直接父类,将一直往上追溯到Object类,调用顺序是自上到下
  • 子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
  • 不能滥用继承,子类和父类之间必须满足 is - a 的关系

继承的本质分析:

继承中,加载类信息时,先加载父类,再加载子类。

方法继承的本质是添加了方法查询链,实际上是在子类中新建了一个同名方法,新建的方法实际上并没有覆盖掉父类的方法,只是阻塞了方法链条,使得访问方法时,在子类中就可以访问到该方法了,不会再顺着链条往上找,从宏观来看,就好像父类的方法被覆盖了。

属性继承的本质是在子类对象中开辟空间存储父类属性,即时同名也会继承过来写入内存,这是实实在在的继承,底层操作与概念一致

在这里插入图片描述

Son son = new Son();
System.out.println(son.name);
/*
这时要按照查找关系来返回信息(直接调用方法规则或this与此类同,super直接从父类查找)
(1)首先看子类是否有该属性
(2)如果子类有这个属性,则返回信息
(3)如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性并且可以访问,就返回信息;如果父类有该属性但无法访问,就报错)
(4)如果父类没有就按照(3)的规则,继续找上级父类,直到Object
*/

多态!!!

重写/覆盖(override)

子类有一个非静态方法和父类的某个非静态方法的名称,返回类型,形参列表一样,那么我们就说子类的这个方法覆盖了父类的那个方法

细节:

  • 子类的方法的形参列表,方法名称,要和父类的参数,方法名称完全一样
  • 子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类。比如父类返回类型是Object,子类方法的返回类型是String。
  • 子类方法不能缩小父类方法的访问权限
  • 如果子类与父类的形参列表或方法名称不一样,那么就不是重写。如果一样且返回类型不一样或不兼容,那么会报错。

基本介绍

多态--------多种形态。方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

方法的多态

重载和重写就体现多态

对象的多态
  • 一个对象的编译类型和运行类型可以不一致

  • 编译类型在定义对象时就确定了,无法改变

  • 运行类型是可以变化的

  • 编译类型看定义时 = 号的左边,运行类型看 = 号的右边

    Animal animal = new Dog();//animal编译类型是Animal,运行类型是Dog
    animal = new Cat();//animal的运行类型变成了Cat,编译类型仍然是Animal
    

多态的细节

**多态的前提是:**两个对象(类)之间存在继承关系

多态的向上转型

  • 本质:父类的引用指向子类的对象

  • 语法:父类类型 引用名 = new 子类类型();

  • 特点:编译类型看左边,运行类型看右边;

    ​ 可以调用父类中的所有成员(需遵守访问权限);

    ​ 不能调用子类中特有成员;

    ​ //若想调用子类中特有成员,子类可以在一个重写方法中调用特有方法,父类引用再调用这个重写的方法

    ​ 最终运行效果看子类的具体实现。

  • 在编译时一个方法能不能调用,首先要看编译类型中有没有该方法?,若有,则在遵循访问权限的情况下直接调用该方法;若编译类型中没有该方法,则不能调用,不管运行类型中有没有该方法。

  • 在运行时一个方法的实际调用是从运行类型即子类开始的,沿着继承的查找关系向上级父类依次查找。

多态的向下转型:

  • 语法:子类类型 引用名 = (子类类型) 父类引用; //此时 引用名 的编译类型和运行类型都是子类类型
  • 只能强转父类的引用,不能强转父类的对象
  • 要求父类的引用必须指向的是当前目标类型的对象
  • 当向下转型后,可以调用子类类型中所有的成员

关于属性:

  • 属性没有重写之说!属性的值看编译类型
  • 对象引用在调用属性时,没有动态绑定机制,属性也没有重写一说,编译类型是什么类型,就调用什么类的属性

动态绑定机制!!!

  • 当调用对象方法时,该方法会和该对象的内存地址/运行类型绑定,该方法的方法体中调用的其他方法也和该对象的运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明、哪里使用

多态的应用

(1)多态数组。数组的定义类型为父类型,里面保存的实际元素类型为子类型

(2)多态参数。方法定义的形参类型为父类类型,实参类型允许为子类型

标识符

规范:

包名:xxx.yyy.zzz.iii com.公司名.项目名.业务模块名

变量和方法名:xxxYyyZzz

类名,接口名:XxxYyyZzz

常量名:XXX_YYY_ZZZ

关键字

this

JVM会给每个对象分配一个this,代表当前对象。哪个对象调用,this就代表哪个对象。

  • this关键字可以用来访问本类的属性,方法,构造器
  • this可以用于区分当前类的属性和局部变量
  • 访问成员方法的语法:this.方法名(参数列表)
  • 访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器,此时该语句必须为第一条语句)
  • this不能在类定义的外部使用,只能在类定义的方法中使用

super

基本介绍:

代表父类的引用,用于访问父类的属性,方法,构造器。

基本语法:

  • 访问父类的属性,但不能访问父类的private属性。super.属性名
  • 访问父类的方法,但不能访问父类的private方法。super.方法名(参数列表);
  • 访问父类的构造器:super(参数列表);只能放在构造器的第一句,只能出现一句。

细节:

  • 调用父类的构造器的好处:分工明确,父类的属性由父类初始化,子类的属性由子类初始化
  • 当子类和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super,this,直接访问都是一样的效果
  • super的访问不限于直接父类,如果"爷爷类"和本类中由同名的成员,也可以使用super去访问"爷爷类"的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。
  • 子类的构造方法中必须调用父类的构造方法且要放在方法体的第一行。如果不显示调用,Java隐式默认调用父类的无参数构造器super(),又如果父类没有无参数构造器,则报错,此时子类必须显示调用父类的有参数构造器。父类隐式默认有一个无参数无方法体构造器,当父类显示定义一个构造器时,默认构造器失效。

instanceof

aaa instanceof Xxx, aaa是对象引用,Xxx是类型,如果aaa的运行类型是Xxx或其子类,则结果为true。

四个访问修饰符

  • 可以用来修饰类中的属性,成员方法以及类
  • 只有default和public才能修饰类
全部皆可用本包及包外子类中使用本包(含同包子类)中使用本类中使用
publicprotecteddefaultprivate
同一类中
同一包中
子类
不同包中

static

静态的。static也可以放在访问修饰符的前面。静态方法不会被重写,如果子类有重名重形参重返回的静态方法和静态属性,父类的会被隐藏。

静态变量(类变量):

访问修饰符 static 数据类型 变量名;

1.静态变量在类加载的时候生成,在类消亡的时候销毁。即使没有创建对象,也可以使用。

2.静态变量会被类的所有实例化对象所共享。

3.静态变量存储在堆或方法区的一块独立空间中,类的对象空间中有对它的引用(即存储其地址)。

4.静态变量的访问权限也应遵循访问修饰符的规定,与普通成员变量一样。

5.静态变量可以用对象名 . 访问,也可以用类名 . 访问,非静态变量不能用类名 . 访问。

静态方法(类方法):

访问修饰符 static 数据返回类型 方法名(){ }

1.类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:类方法中无this的参数,普通方法中隐含着this的参数。

2.静态方法的访问权限也应遵循访问修饰符的规定,与普通成员方法一样。

3.静态方法可以用对象名 . 访问,也可以用类名 . 访问,非静态方法不能用类名 . 访问。

4.类方法中不允许使用和对象有关的关键字,比如this和super。普通方法可以。

5.静态方法只能访问静态成员,不能访问普通成员。(因为静态成员加载时间早,普通成员加载时间晚)

6.普通成员方法,既可以访问普通成员,也可以访问静态成员

7.当方法中不涉及到任何和对象相关的成员时,则可以将方法设计成静态方法,提高开发效率。比如Math类。

8.开发自己的工具类时,可以将方法做成静态的,方便调用,

final

最终的。可以修饰类、属性、成员方法、局部变量

作用:

修饰类则类不可被继承,修饰成员域则成员域为常量且不可再次赋值,修饰成员方法则该方法不可被子类重写,修饰局部变量则不可修改称为局部常量。

细节:

  • final修饰的普通属性必须初始化(初始化位置:定义时;构造器中;普通代码块中),并且以后不可再修改。
  • final修饰静态属性时(初始化位置:定义时,静态代码块中)。
  • final与static配合使用修饰属性时,效率更高,该属性被调用时不会导致类加载(底层编译器做了优化)
  • 包装类和String都是final类不可被继承。

abstract

只能修饰类或方法,修饰类则为抽象类,修饰方法则为抽象方法。

extends

继承

implements

实现(接口)

transient

瞬间的,短暂的,表示该属性不被序列化

synchronized

线程同步

代码块

基本介绍:

代码块又称为初始化块,属于类中的成员(是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。和方法不同的是,代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是在创建对象或加载类时,被隐式调用。

基本语法:

[修饰符] {

​ 代码

};

注意:

  • 修饰符是可选项,如果要写,只能写static
  • 代码块分为两类,使用static修饰的是静态代码块。没有static修饰的是普通代码块
  • 分号 ; 可以省略

好处:

在创建对象时,代码块的调用顺序优先于构造器。若构造器中有大量重复语句,可以将其封装至代码块中,以为构造器的补充。

细节:

  • static代码块的作用是对类初始化,随着类的加载而执行,并且只会执行一次。普通代码块的话,随着对象的创建而执行,每创建一个对象就会执行一次。

  • 类什么时候会被加载:

    • 第一次创建该类的对象实例时;

    • 第一次创建该类的子类对象实例或子类访问父类的静态成员或子类访问子类的静态成员时,若父类被加载过,则不再重新加载父类,否则父类也会被加载并且顺序优先于子类的加载;

    • 使用类的静态成员时,若没加载过则加载;

  • 如果只是使用类的静态成员,普通代码块并不会执行。类加载完成后,才能访问静态变量。

  • 创建一个对象时,在一个类中的属性和方法的调用和访问顺序是:

    • 一,静态属性初始化和调用静态代码块(二者优先级一样,按定义顺序调用)//加载类信息
    • 二,普通属性初始化和调用普通代码块(二者优先级一样,按定义顺序调用)//创建对象
    • 三,调用构造器。
  • 构造器的最前面其实隐含了super()和调用普通代码块。比如在父类子类加载之后,创建子类对象,调用顺序是:

    1.父类的父类的构造方法

    2.父类的普通代码块

    3.父类的构造方法

    4.子类的普通代码块

    5.子类的构造方法。

  • 未加载子类父类时,创建一个子类对象时,各元素的调用顺序是:

    1.父类的静态代码块和静态属性(二者优先级一样,按定义顺序调用)//加载父类

    2.子类的静态代码块和静态属性(二者优先级一样,按定义顺序调用)//加载子类

    //创建子类对象就是开辟空间,空间中有继承自父类的元素,先初始化父类的元素,再初始化子类的

    3.父类的普通代码块和普通属性初始化(二者优先级一样,按定义顺序调用)

    4.父类的构造方法

    5.子类的普通代码块和普通属性初始化(二者优先级一样,按定义顺序调用)

    6.子类的构造方法

  • 静态代码块只能调用静态成员,普通代码块可以调用任意成员,与static修饰方法的规则类似

设计模式

单例设计模式

单例(单个的实例):
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。

单例模有两种方式:1)饿汉式[不存在线程安全问题] 2)懒汉式[存在线程安全问题]

  • 饿汉式单例模式实现步骤:(之所以叫饿汉式,单例随着类的加载而生成,但之后才有可能用到,因这种急切所以叫饿汉)

1)构造器私有化

2)类的内部创建对象(该对象应该用static修饰)

3)对外提供一个静态的获取上面对象的公共方法

4)代码实现

  • 懒汉式单例模式实现步骤:(之所以叫懒汉式,单例不随着类加载而生成,步骤3所说的方法第一次被调用时才生成单例,后续调用返回第一次创建的对象,临到用时才创建,所以叫懒汉式,不会浪费资源)

1)构造器私有化

2)类的内部定义本类类型的引用变量(没有初始化即没有创建对象,引用变量仍用static修饰)

3)对外提供一个静态的有着判空再创功能的返回对象的公共方法

4)代码实现

模板设计模式-抽象类最佳实践

设计一个抽象类(Template),能完成如下功能:

1)编写方法caculateTime(),可以计算某段工作代码的耗时时间

2)编写抽象方法job()

3)编写一个子类Sub,继承抽象类Template,并重写job方法

4)编写一个测试类Test,观察效果

abstract class Template{
    public abstract void job();//抽象方法
    public void caculateTime(){//统计耗时
        //currentTimeMillis返回当前时间距离1970-01-01 00:00:00 的时间差,单位为ms
        long start = System.currentTimeMillis();
        job();
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end - start));
    }
}

抽象类

  • abstract修饰的类即为抽象类,抽象类中可以没有抽象方法,但含有抽象方法的类一定要声明为抽象类。
  • 抽象方法不能有方法体,连{}也不能有。
  • 抽象方法不能使用peivate、final、static来修饰,这些关键字与重写相违背。
  • 抽象类不能使用new关键字来创建对象,它是用来让子类继承的。 //抽象类中的构造方法是为了让子类调用
  • 子类继承抽象类,就一定要实现即重写抽象类中所有的抽象方法,否则子类也要声明为抽象类。

接口

**基本介绍:**难的是不知道什么时候使用接口!(规范方法名,多个类中要编写的功能类似,不同类调用方便)

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

语法:

interface 接口名{
    //属性
    //没有构造方法
    //方法
}

好处:

设计好各种规范(方法),让其它类去实现这些方法。

接口在一定程度上实现代码解耦【即接口规范性+动态绑定】

细节:

  • 接口需要满足like - a的关系:像一个xx一样。

  • 接口不能被实例化。

  • 接口的访问修饰符只能是public或默认,这点和类的访问修饰符是一样的。

  • 接口中的方法都默认被public修饰,接口中的抽象方法可以不用abstract修饰。

  • 在jdk7及之前,接口中不能含有非抽象方法;在jdk8及以后,接口中可以有非抽象方法(有方法体):default修饰的默认方法(放在public之前),static修饰的静态方法。

  • 接口中的属性都默认被 public static final修饰

  • 实现接口的类一定要实现即重写接口中的所有方法,除非是抽象类。

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

  • 接口中属性的访问形式:接口名.属性名

  • 接口不能继承其他的类,但是可以继承(extends)别的接口。

接口的多态性:

接口类型的引用可以指向实现了该接口的类的对象。也存在向下转型

接口的多态传递:

interface A{}
interface B extends A{}
class C implements B{//相当于C也实现了A接口
    
}

内部类

成员内部类

  • 在类的内部定义,与成员域,成员方法同级别的类。可以添加任意访问修饰符。

  • 在外部类中使用内部类时,可以 直接创建对象,然后通过对象使用相关的方法。

  • 在外部其他类中使用内部类时:

    可以通过外部类对象来实例化内部类,Outer.Inner inner = new Outer().new Inner();

    也可以在外部类中定义一个方法用来返回一个内部类对象,需要使用内部类对象时,直接调用该方法。

  • 内部类方法访问外部类属性时可以直接访问,不破坏封装性。但如果访问外部类中与内部类重名的属性,则要通过Outer.this.shuxing访问,否则遵循就近原则。

  • 除了静态常量以外,内部类中不能定义其他静态成员。

静态内部类

  • static 修饰的内部类,类中也只有内部类可以被 static 修饰。可以添加任意访问修饰符。

  • 与外部类同级别,静态内部类里可以定义静态成员。可以直接访问外部类的所有的静态成员。如果重名则 Outer.成员访问。

  • 静态内部类不能直接访问外部类的非静态属性,必须通过外部类对象来访问。

  • 外部类中其他方法或代码块想要访问内部类,直接创建对象访问即可。

    Inner inner = new Inner();

  • 外部其他类中想访问内部类:

    创建内部类对象访问内部类中的成员Outer.Inner inner = new Outer.Inner();

    访问内部类中的静态成员,可直接 Outer.Inner.静态成员名

    也可以在外部类中定义一个方法用来返回一个内部类对象,需要使用内部类对象时,直接调用该方法。

局部内部类

  • 定义在外部类局部位置(地位相当于局部变量)中,比如方法,作用范围和创建对象范围仅限于当前方法(局部位置)中。
  • 可以直接访问外部类的所有成员,包括私有的。
  • 不能添加访问修饰符,但是可以用final修饰。
  • 外部类想要使用内部类定义的方法,可以在该内部类所在方法或代码块中实例化内部类,通过该对象调用其方法。
  • 如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,使用 外部类名.this.成员 去访问。
  • ?局部内部类访问外部类当前方法中的局部变量时,因无法保证变量的生命周期与自身相同,变量必须修饰为final,jdk1.8以后,java默认局部变量被final修饰。
  • ?除了静态常量以外,局部内部类中不能定义其他静态成员。

匿名内部类!!!

  • 没有类名的局部内部类

  • 必须继承一个父类或实现一个接口

  • 定义类,实现类,创建对象的语法合并,只能创建一个该类的对象

  • 局部内部类 + 继承/实现 + 多态 + 存在动态绑定机制

  • 基本语法:

    new 要继承的类的类名||要实现的接口的接口名(参数列表) {
        匿名内部类的类体
    };//一个对象
    /*
    底层实现:
    创建了一个类继承了上述类或实现了上述接口,
    类体即为上述匿名内部类的类体,
    类名为系统自动分配,一般为  外部类类名 + $ + 数字,例如Outer$1
    类创建好后,立即创建一个对象
    之后该类就找不到了,即只能用一次
    */
    
  • 基于类的匿名内部类时,可以有参数列表,实参会传递给类中的构造器。

  • 匿名内部类可以当作实参

枚举

介绍

  • enumeration 简写enum
  • 枚举是一组常量的集合
  • 枚举是一种特殊的类,里面只包含一组有限的特定的对象

实现方式

  • 自定义类实现枚举

    • 构造器私有化,不提供setXxx方法,因为枚举对象值通常为只读
    • 在类中创建好有限的对象,称为枚举对象
    • 对枚举对象使用 访问修饰符 + static + final 共同修饰,实现底层优化
    • 枚举对象名通常使用全部大写,常量的命名规范
    • 枚举对象可以有多个属性
  • enum关键字实现枚举

    • 默认继承Enum类,默认final修饰
    • 如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略
    enum Season {
        SPRING("春天","温暖"),SUMMER("夏天","炎热"),FALL("秋天","凉爽"),WINTER("冬天","寒冷");//必须第一行
        //等价于public static final Season SPRING = new Season("春天","温暖");
        private String name;
        private String desc;
    
        Season(String name, String desc) {//默认被private修饰
            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 + '\'' +
                    '}';
        }
    }
    

常用方法

  • toString:Enum类已经重写过了,返回的是当前对象名
  • name:返回当前对象名(常量名),子类不能重写
  • oridinal:返回当前对象的位置号,默认从0开始
  • values:返回当前枚举类中所有的常量,是个数组
  • valueOf:在已有的常量名中查找传入的字符串,找到就返回该枚举对象,找不到就报错
  • compareTo:比较两个枚举常量的位置号

注解

注解的理解

  • 注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
  • 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码的补充信息
  • 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置。

基本的Annotation介绍

使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。源码中类型是@interface的就是注解。

  • 三个基本的Annotation:
    • @Override:限定某个方法,是重写父类方法,该注解只能用于方法(源码:@Target(ElementType.METHOD))
    • @Deprecated:用于表示某个程序元素(包、类、构造器、方法、属性、字段等)已经过时
    • @SuppressWarnings({“”}):抑制编译器警告,在{“”}中写入希望抑制的一个或多个警告信息
  • 其他注解
    • @Test:修饰某个方法。可以不必在main方法中调用才能执行,可以直接单独执行该方法,方便测试。

元注解

JDK的元注解用来修饰其他注解。

种类:

  • Retention //指定注解的作用范围,SOURCE(源码)、CLASS(class文件)、RUNTIME(运行时)
  • Target //指定注解可以修饰哪些程序元素
  • Document //指定该注解是否会在javadoc体现
  • Inherited //子类会继承父类注解

异常

介绍

  • 关键字:Exception
  • 分为运行时异常和编译时异常,后者必须处理
  • Ctrl+Alt+T快捷键生产try-catch-finally代码块

异常体系图:

在这里插入图片描述

五大运行时异常

  • NullPointException空指针异常,应用程序通过引用变量访问其所指对象,但引用变量为null。

  • ArithmeticException数学运算异常,出现异常的运算条件时,抛出此异常。例如除数为0。

  • ArrayIndexOutOfBoundsException数组下标越界异常。

  • ClassCastException类型转换异常

    class A{}
    class B extends A{}
    class B extends A{}
    public class Test {
        public static void main(String[] args){
            A a = new B();//向上转型
            B b = (B)a;//向下转型
            C c = (C)a;//抛出类型转换异常
        }
    }
    
  • NumberFormatException数字格式不正确异常,当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。

异常处理方式(二选一)

  • try-catch-finally,程序员在代码中捕获的异常,自行处理。

    运行时异常程序员不显式处理的话,默认throws。编译时异常必须显式处理。

    try{//监控区域,异常发生,try块后续代码不再执行
        
    }catch(Exception e){//捕获特定异常,则作出相应操作
        
    }finally{
    //处理善后工作,无论是否有异常都执行,即便try或catch中有返回语句(返回语句执行但不返回,如果finally中没有返回语句,则仍然通过try或catch中的返回语句返回,finally中的语句不影响返回结果)也必须执行finally
        
    }
    

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

​ 可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会在finally执行完毕后直接崩掉。应用场景:执行一段代码,希望不管是否发生异常,都必须执行某个业务逻辑。

  • throws,将发生的异常抛出,可以抛出多个异常,逗号分隔,交给调用者来处理,最顶级的处理者是JVM,JVM的处理策略是输出异常信息并退出程序

自定义异常

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

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

3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException,有默认处理机制,使用方便)

public class Test{
    psvm{
        int age = 80;
        if(!(age >= 18 && age <= 120)){
            throw new AgeException("年龄需要在 18~120 之间")  //关键字throw
        }
    }
}
class AgeException extends RuntimeException{
    public AgeException(String message){
        super(message);
    }
}

throw vs throws

意义位置后面跟的东西
throws异常处理的一种方式方法声明处异常类型
throw手动生成异常对象的关键字方法体中异常对象

其他

  • 子类重写父类的方法时,对抛出异常的规定:子类重写的方法所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的子类型。

  • 在方法体中通过throw关键字抛出异常,如果在当前抛出异常的方法中处理异常,就使用try-catch语句捕获并处理。否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。在此方法的调用者中捕获并处理异常或继续向上抛出

常用类

Object类

  • 所有类的直接或间接父类 ,位于继承树的最顶层
  • Object类可以存储任何对象
    • 作为参数类型,可接收任何对象
    • 作为返回类型,可返回任何对象

getClass方法

  • public final Class<?> getClass(){}

  • 返回引用中存储的实际对象类型

hashCode方法

  • public int hashCode(){}
  • 返回该对象的哈希值,提高具有哈希结构的容器的效率
  • 哈希值是根据对象的地址字符串数字使用hash算法计算出来的int类型的数值
  • 两个引用指向同一个对象,则哈希值肯定一样
  • 两个引用指向不同的对象,则哈希值是不一样的(特殊情况会一样)

toString方法

  • public String toString(){}
  • 返回该对象的字符串表示
  • 可以根据程序需求重写该方法,如:返回对象各个属性值
  • 输出对象时,默认调用对象的toString()方法

equals方法

  • public boolean equals(Object obj){}
  • 默认实现为return this==obj;,比较两个对象地址是否相同
  • 可以重写为比较两个对象的内容是否相同
  • 只能判断引用类型
  • "“既可以判断引用类型,也可以判断基本类型,引用类型与基本类型之间无法用”"比较。65 == 65.0f 为true,65 == 'A’为true

finalize方法

  • 当对象被判定为垃圾对象时,由JVM自动调用该对象的此方法,用以标记垃圾对象,进入回收队列
  • 没有有效引用指向的对象为垃圾对象
  • 垃圾回收:由GC销毁垃圾对象,释放存储空间
  • 自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
  • 手动回收机制:使用System.gc()通知JVM执行垃圾回收
  • 很少重写主动使用

包装类Wrapper

  • 基本数据类型所对应的引用数据类型,前6种的父类为Number

  • byteshortintlongfloatdoublebooleanchar
    ByteShortIntegerLongFloatDoubleBooleanCharacter
  • Integer a = 128;int b = 128;a == b;==判断的是二者的值是否相等

装箱和拆箱

装箱:把基本数据类型包装成引用数据类型

拆箱:把引用数据类型解析成基本数据类型

//以Integer为例说明拆箱和装箱的操作方法
//手动装箱
Integer integer1=new Integer(100);//构造方法
Integer integer2=Integer.valueOf(100);//valueOf方法
//手动拆箱
int i=integer1.intValue();//intValue方法
//自动装箱
int a=100;
Integer integer3=a;//隐式自动调用valueOf方法  Integer integer3=Integer.valueOf(a);
//自动拆箱
int b=integer3;//隐式自动调用intValue方法   int b=integer3.intValue();

基本类型和String类型的转换

基本类型和Wrapper转成字符串
int i = 100;
String str1 = i + "";  //+自动转换
String str2 = Integer.toString(i);  //toString(int i)方法
String str3 = Integer.toString(i,16); //toString(int i,int radix),把i转换成radix进制
Integer integer = 200;
String str4 = integer.toString();   //toString()方法 
String str5 = String.valueOf(integer);  //String类的valueOf(Object obj)方法
String str6 = integer + "";
字符串转成基本类型和Wrapper
String str4 = "100";
Integer j = Integer.parseInt(str4); //parseInt方法,返回int类型,自动装箱
Integer m = new Integer(str4);
String str5 = "true";
boolean flag = Boolean.parseBoolean(str5);//str=="true",则flag==true;str==非"true",则flag==false

Integer缓冲区

  • Java预先创建了256个常用的Integer类型对象(-128~127)
  • 在实际应用中,如果int类型数值范围在-128~127之间,则其调用valueOf装箱时,从Integer缓冲区中返回对应数值的Integer对象,实现复用,如果不在这个数值区间,则返回new Integer(i);

String类

细节

  • String类型初始化后是常量,不可以修改。指的是value存放的引用对象的地址不可修改。value数组中单个字符的值可以修改

  • String类是final类,不可继承

  • String类有属性 private final char value[],用于存放字符串内容

实现的接口

  • Serializable:说明String可以串行化,可以在网络传输
  • Comparable:说明String对象可以比较
  • CharSequence

字符串常量池

  • 字符串池在方法区中,引用变量在栈中,对象在堆中

  • 字符串字面值存储在字符串池中,可以共享

  • String s = "Hello";
    //查看常量池种是否有"Hello"数据空间,如果有,直接指向;如果没有则创建,然后指向。s直接指向常量池。
    String d = "Hello";
    

//d也指向s所指向的"Hello"数据空间,即s==d


- ```java 
String t = new String("Java");
//先在堆中创建空间,里面维护了value属性,指向常量池的"Java"空间,如果没有则创建;有则直接通过value指向。t指向堆中的空间
String f = new String("Java");
//先在堆中创建空间,里面维护了value属性,因为常量池中已经有"Java",所以常量池中不再创建,f指向堆中空间,堆中的value再指向常量池中的空间
//故t==f为false,t.equals(f)为true,t和f在堆中的对象指向常量池中的同一个对象

字符串相加

String c = "hello" + "abc";//等价于String c = "helloabc";
//字面量相加,编译器做了优化,在池中只生成一个"helloabc"空间,c指向它。
String a = "hello";
String b = "abc";
String c = a + b;
/*
两个字符串变量相加或一个字符串变量和一个字面量相加,底层调用的是StringBuilder的append()方法。
StringBuilder sb = new StringBuilder(); 
sb.append(a); 
sb.append(b); 
sb.toString();  
toString()会new一个String对象返回。所以变量相加是在堆中,c指向堆中的对象,对象中的value指向池中的"helloabc"。
*/

String类常用方法

  • public int length():返回字符串的长度。
  • public char charAt(int index):根据下标获取字符。
  • public boolean contains(String str):判断当前字符串是否包含str。
  • public char[] toCharArray():将字符串转换成数组。
  • public int indexOf(String str):查找str首次出现的下标,存在,则返回该下标;不存在,则返回-1。
  • public int lastIndexOf(String str):查找字符串在当前字符串中最后一次出现的下标索引。
  • public String trim():去掉字符串前后的空格。
  • public String toUpperCase():将小写转成大写。
  • public boolean endWith(String str):判断字符串是否以str结尾 。
  • public String replace(char oldChar,char newChar):将旧字符替换为新字符。返回的新new对象
  • public String[] split(String str):根据str做拆分。str:(" “) //根据空格拆分 (”[ ,]“) //根据空格和逗号拆分 (”[ ,]+")//可以有多个空格。
  • public int compareTo(String str):比较大小(字典表中的顺序),当前字符串的值减去str的值,差作为结果返回。
  • public String intern():如果池中已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。总之返回的都是常量池中的地址(对象)
  • public String format(String str):格式化字符串。占位符:%s字符串,%c字符,%d整数,%.2f浮点数(保留两位小数),类似于C语言的printf。

StringBuffer类

如果字符串存在大量的修改动作,一般使用StringBuffer和StringBuilder。

基本介绍

  • java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。
  • 很多方法与String相同,但StringBuffer是可变长度的。
  • StringBuffer是一个容器。

细节

  • StringBuffer的直接父类是AbstractStringBuilder。
  • StringBuffer实现了Serializable,即StringBuffer的对象可以串行化。
  • 在父类AbstractStringBuilder中有属性 char[] value,不是final修饰的。该value数组存放字符串内容,存放在堆中。
  • StringBuffer是一个final类,不能被继承。

String VS StringBuffer

  • String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低//private final char[] value
  • StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用每次更新地址,效率较高

构造器

  • StringBuffer(); //value[] 大小为16
  • StringBuffer(int n); //n指定value数组大小
  • StringBuffer(String str); //value[] 大小为str.length + 16

String和StringBuffer相互转换

  • String->StringBuffer

    • String s = "hello";
      StringBuffer b1 = new StringBuffer(s);
      
    • StringBuffer b2 = new StringBuffer();
      b2.append(s);
      
  • StringBuffer->String

    • String s2 = b1.toString();
    • String s3 = new String(b1);

常用方法

  • public void append(String str):追加

    ​ 如果StringBuffer中的原有内容的长度加上str的长度大于StringBuffer的value数组大小,就再创建一个扩容后的数组,原有内容和str放进新数组中,并将地址返回给value。

    ​ 否则,就将str的value数组中的内容赋给StringBuffer的value数组,从value.length开始。

  • public void insert(int index,String str):插入

  • public void replace(int start,int end,String str):替换,含头不含尾

  • public void delete(int start,int end):删除

  • public int indexOf(String str):查找str在字符串第一次出现的索引,如果找不到则返回-1

  • public int length():获取长度

StringBuilder类

  • StringBuilder和StringBuffer均代表可变的字符序列,方法是一样的,所以使用和StringBuffer一样。比StringBuffer快一点。
  • 二者实现的接口和继承的父类都一样。
  • StringBuilder的方法没有synchronized关键字,线程不安全,因此在单线程的情况下使用。

Math类

常用方法

  • public static int abs(int i);//求绝对值

  • public static double pow(double a,duoble b );//求a的b次方

  • public static double ceil(double a);a向上取整并返回

  • public static double floor(double a);a向下取整并返回

  • public static int round(float a);a四舍五入

  • public static long round(double a);a四舍五入

  • public static double sqrt(double a);a开方

  • public static double random();返回位于[0.0 ,1.0)的随机数

    获取a~b之间的一个随即整数,a,b均为整数。int res = (int)(a + Math.random()*(b-a+1) );

  • public static long/int/float/double max(long/int/float/double a, long/int/float/double b);a,b比较,返回较大值

  • public static long/int/float/double min(long/int/float/double a, long/int/float/double b);a,b比较,返回较小值

Arrays类

常用方法

    • static StringtoString(boolean[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(byte[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(char[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(double[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(float[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(int[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(long[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(Object[] a) 返回指定数组的内容的字符串表示形式。
      static StringtoString(short[] a) 返回指定数组的内容的字符串表示形式。
    • static voidsort(byte[] a) 按照数字顺序排列指定的数组。
      static voidsort(byte[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(char[] a) 按照数字顺序排列指定的数组。
      static voidsort(char[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(double[] a) 按照数字顺序排列指定的数组。
      static voidsort(double[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(float[] a) 按照数字顺序排列指定的数组。
      static voidsort(float[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(int[] a) 按照数字顺序排列指定的数组。
      static voidsort(int[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(long[] a) 按照数字顺序排列指定的数组。
      static voidsort(long[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static voidsort(Object[] a) 对指定对象升序排列的阵列,根据natural ordering的元素。
      static voidsort(Object[] a, int fromIndex, int toIndex) 对指定对象升序排列的数组的指定范围内,根据natural ordering的元素。
      static voidsort(short[] a) 按照数字顺序排列指定的数组。
      static voidsort(short[] a, int fromIndex, int toIndex) 按升序排列数组的指定范围。
      static <T> voidsort(T[] a, Comparator<? super T> c) 根据指定的比较器引发的顺序对指定的对象数组进行排序。 基于接口的匿名内部类,重写compare方法,根据方法返回值是否大于0来影响顺序。
      static <T> voidsort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c) 根据指定的比较器引发的顺序对指定的对象数组的指定范围进行排序。

binarySearch要求数组是有序的,找不到指定值,返回 -(low+1);//low为指定值在排序中应该在的位置

    • static intbinarySearch(byte[] a, byte key) 使用二进制搜索算法搜索指定值在指定字节数组的索引。
      static intbinarySearch(byte[] a, int fromIndex, int toIndex, byte key) 使用二进制搜索算法搜索指定值的指定字节数组的范围。
      static intbinarySearch(char[] a, char key) 使用二进制搜索算法搜索指定数组的指定值。
      static intbinarySearch(char[] a, int fromIndex, int toIndex, char key) 使用二分搜索算法搜索指定值的指定数组的范围。
      static intbinarySearch(double[] a, double key) 使用二进制搜索算法搜索指定值的指定数组的双精度值。
      static intbinarySearch(double[] a, int fromIndex, int toIndex, double key) 使用二分搜索算法搜索指定值的指定数组的双精度范围。
      static intbinarySearch(float[] a, float key) 使用二叉搜索算法搜索指定数组的浮点数。
      static intbinarySearch(float[] a, int fromIndex, int toIndex, float key) 使用二分搜索算法搜索指定数组的浮点数范围。
      static intbinarySearch(int[] a, int key) 使用二叉搜索算法搜索指定的int数组的指定值。
      static intbinarySearch(int[] a, int fromIndex, int toIndex, int key) 使用二叉搜索算法搜索指定值的指定数组的范围。
      static intbinarySearch(long[] a, int fromIndex, int toIndex, long key) 使用二分搜索算法搜索指定值的指定数组的范围。
      static intbinarySearch(long[] a, long key) 使用二进制搜索算法搜索指定数组的指定数组。
      static intbinarySearch(Object[] a, int fromIndex, int toIndex, Object key) 使用二进制搜索算法搜索指定对象的指定数组的范围。
      static intbinarySearch(Object[] a, Object key) 使用二叉搜索算法搜索指定对象的指定数组。
      static intbinarySearch(short[] a, int fromIndex, int toIndex, short key) 使用二进制搜索算法搜索指定值的指定数组的短整型范围。
      static intbinarySearch(short[] a, short key) 使用二进制搜索算法搜索指定值的指定数组的指定值。
      static <T> intbinarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) 使用二进制搜索算法搜索指定对象的指定数组的范围。
      static <T> intbinarySearch(T[] a, T key, Comparator<? super T> c) 使用二叉搜索算法搜索指定对象的指定数组。
    • static boolean[]copyOf(boolean[] original, int newLength) 使用 false (如有必要)复制指定的数组,截断或填充,以使副本具有指定的长度。
      static byte[]copyOf(byte[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static char[]copyOf(char[] original, int newLength) 复制指定的数组,截断或填充空字符(如有必要),以便复制具有指定的长度。
      static double[]copyOf(double[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static float[]copyOf(float[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static int[]copyOf(int[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static long[]copyOf(long[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static short[]copyOf(short[] original, int newLength) 复制指定的数组,用零截取或填充(如有必要),以便复制具有指定的长度。
      static <T> T[]copyOf(T[] original, int newLength) 复制指定的数组,用空值截断或填充(如有必要),以便复制具有指定的长度。
      static <T,U> T[]copyOf(U[] original, int newLength, 类<? extends T[]> newType)
    • static voidfill(boolean[] a, boolean val) 将指定的布尔值分配给指定的布尔数组的每个元素。
      static voidfill(boolean[] a, int fromIndex, int toIndex, boolean val) 将指定的布尔值分配给指定数组布尔值的指定范围的每个元素。
      static voidfill(byte[] a, byte val) 将指定的字节值分配给指定字节数组的每个元素。
      static voidfill(byte[] a, int fromIndex, int toIndex, byte val) 将指定的字节值分配给指定字节数组的指定范围的每个元素。
      static voidfill(char[] a, char val) 将指定的char值分配给指定的char数组的每个元素。
      static voidfill(char[] a, int fromIndex, int toIndex, char val) 将指定的char值分配给指定的char数组的指定范围的每个元素。
      static voidfill(double[] a, double val) 将指定的double值分配给指定的双精度数组的每个元素。
      static voidfill(double[] a, int fromIndex, int toIndex, double val) 将指定的double值分配给指定的双精度数组范围的每个元素。
      static voidfill(float[] a, float val) 将指定的float值分配给指定的浮点数组的每个元素。
      static voidfill(float[] a, int fromIndex, int toIndex, float val) 将指定的浮点值分配给指定的浮点数组的指定范围的每个元素。
      static voidfill(int[] a, int val) 将指定的int值分配给指定的int数组的每个元素。
      static voidfill(int[] a, int fromIndex, int toIndex, int val) 将指定的int值分配给指定的int数组的指定范围的每个元素。
      static voidfill(long[] a, int fromIndex, int toIndex, long val) 将指定的long值分配给指定的longs数组的指定范围的每个元素。
      static voidfill(long[] a, long val) 将指定的long值分配给指定的longs数组的每个元素。
      static voidfill(Object[] a, int fromIndex, int toIndex, Object val) 将指定的对象引用分配给指定的对象数组的指定范围的每个元素。
      static voidfill(Object[] a, Object val) 将指定的对象引用分配给指定的对象数组的每个元素。
      static voidfill(short[] a, int fromIndex, int toIndex, short val) 将指定的短值分配给指定的短裤数组的指定范围的每个元素。
      static voidfill(short[] a, short val)
    • static booleanequals(boolean[] a, boolean[] a2) 如果两个指定的布尔数组彼此 相等 ,则返回 true
      static booleanequals(byte[] a, byte[] a2) 如果两个指定的字节数组彼此 相等 ,则返回 true
      static booleanequals(char[] a, char[] a2) 如果两个指定的字符数组彼此 相等 ,则返回 true
      static booleanequals(double[] a, double[] a2) 如果两个指定的双精度数组彼此 相等 ,则返回 true
      static booleanequals(float[] a, float[] a2) 如果两个指定的浮动数组彼此 相等 ,则返回 true
      static booleanequals(int[] a, int[] a2) 如果两个指定的int数组彼此 相等 ,则返回 true
      static booleanequals(long[] a, long[] a2) 如果两个指定的longs数组彼此 相等 ,则返回 true
      static booleanequals(Object[] a, Object[] a2) 如果两个指定的对象数组彼此 相等 ,则返回 true
      static booleanequals(short[] a, short[] a2) 如果两个指定的短裤阵列彼此 相等 ,则返回 true
    • static <T> List<T>asList(T... a) 返回由指定数组支持的固定大小的列表。

System类

  • static void arrayCopy(Object src,int srcPos,Object dest,int destPos,int length)//复制数组
    //src:源数组  srcPos:从哪个位置开始复制   dest:目的数组  destPos:复制到哪个位置   length:复制的长度  
    
  • static long currentTimeMillis()//返回自1970年1月1日0时0分0秒以来的毫秒数

  • static void gc()//通知垃圾回收器回收

  • static void exit(int status)//退出jvm,参数为0表示正常退出,非0表示异常退出

需要处理大整数时使用,当数据非常大时构造器中的参数可以为字符串类型

BigDecimal类

  • 位于java.math包中

  • 用于精确计算精度极高的浮点数

  • 构造方法:BigDecimal bd = new BigDecimal("1.0");

  • 方法:

    • BigDecimal add(BigDecimal bd) //加法

    • BigDecimal subtract(BigDecimal bd) //减法

    • BigDecimal multiply(BigDecimal bd) //乘法

    • BigDecimal divide(BigDecimal bd) //除法

    • BigDecimal divide(BigDecimal bd,int scal,RoundingMode mode)  
      //除不尽时,scal为小数保留位数,mode为舍入方式,四舍五入为BigDecimal.ROUND_HALF_UP,scal不写的话默认为被除数小数点
      //后的位数
      

第一第二代日期类

Date类

java.util.Date

表示特定的瞬间,精确到毫秒,大多数方法已被Calendar类中的方法所取代

getTime()方法:返回自1970年1月1日0时0分0秒以来的毫秒数

after(Date d)方法:判断当前Date是否在d之后

before(Date d)方法:判断当前Date是否在d之前

compareTo(Date d)方法:两个Date相减

Calendar类

  • 通过Calendar.getInstance()获取对象
  • getTime(),返回Date类对象
  • getTimeInMillis(),返回自1970年1月1日0时0分0秒以来的毫秒数
  • get(int field),返回年或月(0-11)或日或时或分或秒,field是Calendar定义的常量,例如Calendar.YEAR
  • set(int field,int i),修改时间某个字段,field同上,i为修改值
  • add(int field,int i),增加时间某个字段,field同上,i为修改值
  • getActualMaximum(int field),获取该时间字段的最大值
  • getActualMinimum(int field),获取该时间字段的最小值

SimpleDateFormat类

  • 一种格式类
  • SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//分隔符可以自定义
  • String str = sdf.format(date); //格式化Date类型的date,把日期转成字符串
  • Date date2 = sdf.parse("2000-12-18 23:01:45");//解析,把日期格式的字符串转换成日期

在这里插入图片描述

第三代日期类

LocalDate(日期)、LocalTime(时间)、LocalDateTime(日期时间),JDK8加入

LocalDateTime ldt = LocalDateTime.now();//获得当前日期时间的对象
int year = ldt.getYear();//得到年份
//其他类同

DateTimeFormatter格式日期类

类似于SimpleDateFormat

LocalDateTime ldt = LocalDateTime.now();//获得当前日期时间的对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH点mm分钟ss秒");//括号里是日期格式
String strDate = dtf.format(ldt);//括号里是日期对象

Instant时间戳

类似于Date,提供了一系列和Date类转换的方法

Instant now = Instant.now();
System.out.println(now);
Date date = Date.from(now);
Instant instant = date.toInstant();

更多方法

  • 是否闰年
  • plus增加时间的某个字段
  • minus查看一年前和一年后的日期

Collections类

  • void sort(List<T> list) 排序
  • int binarySerach(List<? extends Comparable<? super T>> list,T key)对已经排好序的list进行二分查找,返回key在list中的位置
  • void copy(List<? super T>dest,List<? super T>src)复制,源list和目标list大小必须相同才能成功复制
  • void reverse(List<?> list)反转list顺序
  • void shuffle(List<?> list)打乱list顺序

集合

概念

对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能。

和数组的区别

  • 数组长度固定,集合长度不固定
  • 数组可以存储基本类型和引用类型,集合只能存储引用类型

位置

java.util.*

注意要点

  • remove()方法:当传入int类型时,调用remove(int index)方法按下标删除元素;当传入引用类型时,例如Integer类型对象,调用remove(Object o)方法,删除元素e ,使得返回值=(o==null ? e==null : o.equals(e)) ,故可以remove(new Integer(20));例子中的Student类的新建对象不能作为remove(Object o)的参数的原因是它没有重写equals()方法,比较的仍是对象地址。
  • 集合不定义泛型时,默认是Object泛型集合,这时方法返回Object类变量

Collection接口

单列集合,实现Collection接口的类都可以使用迭代器。

在这里插入图片描述

使用

//1.创建集合
Collection collection = new ArrayList();
//2.添加元素
collection.add("Apple"); //添加String类型的元素时,可以字面值隐式创建对象;其他类型必须显示创建对象,通过变量名添加
collection.add("iPhone");
collection.add("iPad");
//3.删除元素
collection.remove("Apple");
//String类型可以字面值指代属性为该字面值的对象(字符串常量池);
//其他对象必须以变量名为参数,若以[new 类名(属性值)]为参数,操作无法实现,因新建对象在堆中集合外
//4.遍历元素
//4.1使用增强for循环,注意obj的类型是Object。增强for底层源码依然是迭代器。
for(Object obj:collection){
    String s = (String)obj;
    System.out.println(s);
}
//4.2使用迭代器(迭代器是专门用来遍历集合的一种方式)Iterator是一个接口,Collection接口继承了它。
Iterator it = collection.iterator();
//itit快捷键快速生成和迭代器结合的while循环
While(it.hasNext()){    //hasNext():有没有下一个元素
    String s = (String)it.next();   //next():获取下一个元素,返回类型为Object
    System.out.println(s);
    //collection.remove(s);    //在迭代器运行过程中,使用Collection类的删除方法会抛出异常
    it.remove();   //删除迭代器所指向的元素
}
it = collection.iterator();//其指针现在指向最后一个元素,重置迭代器,使其指针指向首个元素之前
//5.判断
System.out.println(collection.contains("Apple"));  //contains(Object obj)  是否含有obj对象
//注意事项与"删除元素"相同
System.out.println(collection.isEmpty());  //判空
collection.clear();//清空
System.out.println(collection);

List接口

特点

  • 有序
  • 有下标
  • 可以重复

使用

  • 方法代码块
public class TestList {
    public static void main(String[] args) {
        //创建对象
        List lt = new ArrayList();
        //添加对象
        lt.add("华为");
        lt.add("小米");
        lt.add(0,"坚果"); //在index位置添加元素
        System.out.println("元素个数:"+lt.size());
        System.out.println(lt.toString());
        //删除对象
//        lt.remove("坚果");
//        lt.remove(0);   //利用下标删除指定元素
        //清空。略
        //遍历。可以使用增强for循环(略),for循环,Iterator迭代器(略),ListIterator迭代器
        System.out.println("---------------for---------------");
        for (int i = 0; i < lt.size(); i++) {
            System.out.println(lt.get(i));  //利用下标和get(int index)方法可以使用for循环来遍历列表
        }

        ListIterator lit = lt.listIterator();
        System.out.println("------------使用列表迭代器从前往后-----------");
        while (lit.hasNext()){
            System.out.println(lit.nextIndex()+": "+lit.next());
        }
        System.out.println("------------使用列表迭代器从后往前-----------");
        //从前往后遍历完之后,指针指向迭代器最后一个元素之后,此时适合从后往前遍历
        while(lit.hasPrevious()){
            System.out.println(lit.previousIndex()+": "+lit.previous());
        }

        //获取位置
        System.out.println(lt.indexOf("华为"));//根据对象获取下标
        
        //截取。List subList(int fromIndex,toIndex) //含头不含尾
        
        lt.set(1,"友商");//set(int index , Object ele)在索引为1处置为ele,就是替换,索引不能越界
    }
}
  • List转换为数组

    T[] toArray(T[] a) a的长度应该不大于list的长度,否则多出来的为Null

  • 数组转换为list

    • List<T> asList(T...a) 生成的集合不允许增加和删除元素,是受限集合
    • 把基本类型数组转换为集合时,需要把类型修改为包装类型

List实现类

  • ArrayList

    • 数组结构实现,查询快,增删慢
    • jdk1.2版本,运行效率快,线程不安全
    • DEFAULT_CAPACITY=10;默认容量
    • 使用无参构造器后,容量为0;添加一个元素后,扩容为10;添加第11个元素时,扩容为15;每次扩容1.5倍。
    • 使用有参构造器可以直接指定容量;扩容也是每次1.5倍。
    • elementData;存放元素的数组
    • size;实际元素个数
    • 方法 ↓
    public class Test {
        public static void main(String[] args) {
            ArrayList arrayList = new ArrayList();
            Car car1 = new Car("宝马", 400000);
            Car car2 = new Car("奔驰",250);
            //添加
            arrayList.add(car1);
            arrayList.add(car2);
            //删除指定元素
            arrayList.remove(1);//删除奔驰
            System.out.println("删除奔驰");
            //arrayList.remove(new Car("奔驰",250));
            // 若使用对象作为参数去删除,源码是依次按照equals方法比较,相同的删除
    
            //查找元素是否存在
            System.out.println("奔驰在吗"+ arrayList.contains(car2));
            //获取元素个数
            System.out.println("元素个数:" + arrayList.size());
            //清空
            arrayList.clear();
            System.out.println("清空");
            //判断是否为空
            System.out.println("是否为空:" + arrayList.isEmpty());
            //添加多个元素
            ArrayList arrayList1 = new ArrayList();
            arrayList1.add(car1);
            arrayList1.add(car2);
            arrayList.addAll(arrayList1);
            System.out.println("添加多个元素后arrayList=" + arrayList);
            //查找多个元素是否都存在
            System.out.println("arrayLiat1中的元素是否都存在:" + arrayList.containsAll(arrayList1));
            //增强for遍历
            System.out.println("========增强for==========");
            for (Object o : arrayList) {
                System.out.println(o);
            }
            //迭代器遍历
            System.out.println("=======迭代器=========");
            Iterator iterator = arrayList.iterator();
            while (iterator.hasNext()) {
                Car next =  (Car)iterator.next();
                System.out.println(next);
            }
            //删除多个元素
            arrayList.removeAll(arrayList1);
            System.out.println("删除多个元素后:" + arrayList);
        }
    }
    
  • Vector

    • 数组结构实现,查询快,增删慢

    • jdk1.0版本,运行效率慢,线程安全

    • 使用无参构造器后,容量默认为10;添加第11个元素时,扩容为20;每次扩容2倍。

    • 使用有参构造器可以直接指定容量;扩容也是每次2倍。

    • 现多不用

    • 特有遍历方式:枚举器。

      Vector vector = new Vector();
      Enumeration en = vector.elements();//获取枚举器
      while(en.hasMoreElements()){//枚举器是否有更多元素
          String o = (String)en.nextElement();//获取枚举器的下一个元素
          System.out.println(o);
      }
      
  • LinkedList

    • 双向链表结构实现,查询慢,增删快
    • 元素可以重复,可以为null
    • 线程不安全
    • 属性:
      • int size = 0;初始长度
      • Node first;链表头节点
      • Node last;链表尾节点
      • int modCount;
    • 方法:
      • add(E e);添加元素,底层调用linkLast(E e)方法
      • remove(int index);删除指定位置元素。
      • remove();删除首节点,底层调用removeFirst()方法
      • set(int index,E element);替换
      • get(int index);获取
    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

Set接口

  • 无序
  • 没有下标/索引,故不能用普通for循环遍历
  • 元素不能重复
  • 最多包含一个null
  • 增加,删除,遍历,判断与Collection接口的方法相同

Set实现类

  • HashSet类

    • 存储结构:哈希表(数组+链表+红黑树)。当数组大小大于等于64,链表大小大于等于8时,链表就会树化变成红黑树。
    • 节点类型为HashMap$Node<K,V>,有属性hash,key,value,next
    • 存储过程:
      • 添加一个元素时,先得到hash值------会转成------>索引值,找到存储数据表table,看这个位置是否已经有元素存放;
      • 如果没有,直接放入
      • 如果有,调用equals比较并比较hash值:
        • 如果都相同,就放弃添加;
        • 如果有一个不同,则判断该节点是否树节点。若是则按照树方法添加;若不是,则顺着链表依次比较(在此过程中统计链表节点数量,大于等于8则进入树化算法),都不同则添加至末尾,有相同的则替换value。
      • 当数组大小大于等于64,链表大小大于等于8时,链表就会树化变成红黑树。
    • 扩容机制
      • 第一次添加Node时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)=16*0.75=12
      • 如果table数组Node数量到了临界值12(链表上的也算进去),就会扩容到16*2=32,新的临界值就是32*0.75=24,依此类推
      • 在Java8中,如果一条链表的元素个数达到TREEIEF_THRESHOLD(默认是8),并且table的length>=MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树),否则仍然采用数组扩容机制。
      • 如果红黑树上的元素数量较少到了6个,就会剪枝即重新变回链表。
    • 添加,删除,判断等都遵循上述过程
    • 若想在哈希表中实现属性值相同的对象被判定为同一对象,则需在泛型类中重写hashcode方法和equals方法使之与属性值关联
    • 源代码中,创建HashSet对象时,其实是调用了HashMap的构造方法,HashSet的元素是HashMap元素的key
    • 底层是HashMap
  • TreeSet

    • 存储结构:红黑树

    • 基于排列顺序实现元素不重复

    • 不可存放null值

    • 实现了SortedSet接口,对集合元素自动排序

    • 元素对象的类型必须实现Comparable接口,重写compareTo()方法,指定排序规则

    • 通过compareTo方法确定是否重复元素,int compareTo(类型 o ),自定义比较规则,返回int类型数据,小的在前,0则代表重复

    • 遍历:增强for,迭代器Iterator

    • TreeSet使元素不必实现Comparable接口的构造方法:TreeSet(Comparator<? super E> comparator) ,构造一个新的,空的树集,根据指定的比较器进行排序。可以在参数表中构造实现Comparator接口的匿名内部类

      TreeSet<Person> persons = new TreeSet<>(new Comparetor<Person>{//实现Comparator接口的匿名内部类
          @Override
          public int compare(Person p1,Person p2){
              //重写比较方法
              return ...
          }
      })//这个匿名内部类是构造方法的参数
      
    • 源代码中,TreeSet的有参构造器的Comparator参数传给了TreeMap的comparator属性

  • LinkedHashSet

    • 全面说明
      • 是HashSet的子类
      • 底层是一个LinkedHashMap(HashMap的子类),底层维护了一个"数组(hash表)+双向链表"
      • 根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(与HashSet不同的是:HashSet的table数组中每一个元素都是一条链表的头节点,所以HashSet中有多条链表;而LinkedHashSet中按插入次序依次挂载到前一个元素之后,所以只有一条链表),这使得元素看起来是以插入顺序保存的
      • 不允许添加重复元素
      • 在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet中有head和tail),每一个节点有before和after属性。
      • 数组类型是HsahMap N o d e ,链表节点是 L i n k e d H a s h M a p Node,链表节点是LinkedHashMap Node,链表节点是LinkedHashMapEntey类型,后者继承前者

Map接口

双列集合

在这里插入图片描述

方法

public class MapTest {
    public static void main(String[] args) {
        //创建Map集合
        Map<String,String> map = new HashMap<>();
        //添加元素,key重复,替换value,不会新建键值对
        map.put("cn","中国");
        map.put("usa","美国");
        map.put("uk","英国");
        System.out.println("元素个数:"+map.size());
        System.out.println(map.toString());
        /*
        元素个数:3
        {usa=美国, uk=英国, cn=中国}
         */

        //删除元素
//        map.remove("usa");
//        System.out.println("元素个数:"+map.size());//元素个数:2

        //遍历
        //1.使用keySet()遍历
        Set<String> keyset=map.keySet();//返回全部key组成的set集合
        for (String key:keyset) {   //问题转换为对set集合的遍历,也可以使用迭代器
            System.out.println(key+"="+map.get(key));//通过key返回value
        }
        /*
        usa=美国
        uk=英国
        cn=中国
         */
        
        //2使用values()方法
        Collection values = map.values();
        //再使用增强for,略
        
        
        //3.使用entrySet()方法
        Set<Map.Entry<String,String>> entries = map.entrySet();
        //Entry是Map的内部接口,一个Entry对象包含一个键值对,返回全部Entry对象即全部键值对组成的Set集合
        for (Map.Entry<String,String> entry:entries) {
            System.out.println(entry.getKey()+"="+entry.getValue());//键值对的getKey方法和getValue方法
        }
        /*
        usa=美国
        uk=英国
        cn=中国
         */

        //判断
        System.out.println(map.containsKey("cn"));
        System.out.println(map.containsValue("俄罗斯"));
        /*
        true
        false
         */
    }
}

Map实现类

  • HashMap

    • 存储过程和扩容机制几乎与HashSet相同

    • JDK1.2版本,线程不安全,运行效率快

    • 有属性HashMap$Node<K,V>[] table,一个Node<K,V>对象存放键值对

    有属性HashMap E n t r y S e t e n t r y S e t ,该集合里的元素类型是接口 M a p . E n t r y < K , V > ,通常存放该接口实现类 H a s h M a p EntrySet entrySet,该集合里的元素类型是接口Map.Entry<K,V>,通常存放该接口实现类HashMap EntrySetentrySet,该集合里的元素类型是接口Map.Entry<K,V>,通常存放该接口实现类HashMapNode<K,V>类型的对象,这里的对象与table表中的HashMap N o d e < K , V > 类型的对象 h a s h 值相同即为同一个对象, e n t r y S e t 里的是对 t a b l e 表中的 H a s h M a p Node<K,V>类型的对象hash值相同即为同一个对象,entrySet里的是对 table表中的HashMap Node<K,V>类型的对象hash值相同即为同一个对象,entrySet里的是对table表中的HashMapNode<K,V>类型的对象 的指向。

    • 允许用null作为key或value,key为null只能有一个,value为null可以有多个

    • k-v放在HashMap$Node node = newNode(hash,key,value,null)中

    • k-v为了方便程序员的遍历,还创建了EntrySet集合,该集合存放的元素的类型是Map.Entry,而一个Entry对象就有k、v,EntrySet<Map.Entry<K,V>>transient Set<Map.Entry<K,V>> entrySet;,Entry里的k,v指向Node里的k,v。

      Map.Entry<K,V>是一个接口,HashMap N o d e 类实现了它。 e n t r y S e t 方法返回的对象的运行类型是 H a s h M a p Node类实现了它。entrySet方法返回的对象的运行类型是HashMap Node类实现了它。entrySet方法返回的对象的运行类型是HashMapEntrySet,对象里存放的Map.Entry类型的对象其实是实现了Entry接口的Node类型,运用了接口的多态性。这样可以方便调用getKey()和getValue()方法,

    • 方法见上代码块

  • Hashtable

    • JDK1.0版本,线程安全,运行效率慢

    • 不允许null作为key或value

    • 方法基本和HashMap一样

    • 底层有数组Hashtable$Entry[]

    • 第一次添加元素后容量为11,临界值为8,扩容后容量 = (原容量 << 2) + 1

  • Properties

    • Hashtable的子类

    • 要求key和value都是String类型

    • 不能放null

    • 通常用于一些配置文件(xxx.properties)的读取,加载数据到Properties类对象,并进行读取和修改。

  • TreeMap

    • 不可以存放null值

    • 实现了SortedMap接口(Map的子接口),可以对key自动排序

    • TreeMap的元素中的key必须实现Comparable接口并重写compareTo方法,或者通过构造方法

      TreeMap(Comparator<? super E> comparator),构造一个新的,空的树集,根据指定的比较器进行排序。可以在参数表中构造实现Comparator接口的匿名内部类。compare方法返回0的话,替换value。

Properties

概念

属性集合,继承HashTable

特点

  • 存储属性名和属性值
  • 没有泛型,和流有关,可保存在流中或从流中加载
  • 配置文件的格式:
    • 键=值
    • 键=值
  • 键值对不需要有空格,值不需要用引号引起来,默认类型是String

方法

Properties pro = new Properties();  //创建集合
pro.setProperty("user","root");   //添加数据,若该key已经存在则为修改
pro.setProperty("pwd","root");    
String str = pro.s=getProperty(user);  //根据键获取值
//keySet,entrySet遍历
//Set<String> stringPropertyNames()返回属性名集合
//和流有关的方法
//-----list方法-------
PrintWriter pw = new PrintWriter("d:\\print.txt");
pro.list(pw);   //将属性名和值打印到文件中
pw.close;
//-----store方法  保存------
FileOutputStream fos = new FileOutputStream("d:\\mysql.properties");//如果有中文,存储为对应的unicode码
pro.store(fos,"zhushi");
fos.close();
//-----load方法  加载-------
Properties orp = new Properties();
FileIutputStream fis = new FileIutputStream("d:\\mysql.properties");
orp.load(fis);      //将properties文件中的属性加载到Properties对象中
fis.close();

集合的选择

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

2)一组对象(单列):Collection接口

​ 允许重复:List接口

​ 增删多:LinkedList【底层维护了一个双向链表】

​ 改查多:ArrayList【底层维护了Object类型的可变数组】

​ 线程安全:Vector【数组结构实现】

​ 不允许重复:Set

​ 无序:HashSet【底层是HashMap,维护了一个哈希表,即(数组 + 链表 + 红黑树)】

​ 排序:TreeSet【底层是TreeMap】

​ 插入和取出顺序一致:LinkedHashSet【底层维护了数组 + 双向链表,底层是LinkedHashMap】

3)一组键值对(双列):Map接口

​ 键无序:HashMap【底层维护了一个哈希表】

​ 线程安全:Hashtable【不许存储null键或值,底层有数组 + 链表,数组类型为Hashtable$Entry[]】

​ 键排序:TreeMap【底层是红黑树】

​ 键插入和取出顺序一致:LinkedHashMap【底层维护了数组 + 双向链表】

​ 读取文件:Properties【不许存储null】

Collections工具类

常用方法(全为静态)

  • void reverse(List<?> list) 反转指定列表中元素的顺序

  • void shuffle(List<?> list) 使用默认的随机源随机排列指定的列表

  • void sort(List list) 根据其元素的natural ordering对指定的列表进行排序

  • void sort(List list, Comparator<? super T> c) 根据指定的比较器对指定的列表进行排序

  • void swap(List<?> list, int i , int j) 交换指定列表中指定位置的元素

  • T max(Collection<? extends T> coll) 根据其元素的自然顺序返回给定集合的最大元素(即排序最前的元素)

  • T max(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定的比较器返回给定集合的最大元素(即排序最前的元素)

  • T min(Collection<? extends T> coll) 根据其元素的自然顺序返回给定集合的最小元素(即排序最末的元素)

  • T min(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定的比较器返回给定集合的最小元素(即排序最末的元素)

  • int frequency(Collection<?> c, Object o) 返回指定集合中与指定对象相等的元素个数

  • void copy(List<? super T> dest, List<? extends T> src) 将所有元素从一个列表复制到另一个列表中, dest.size()不能小于src.size()

  • boolean replaceAll(List list, T oldVal, T newVal) 将列表中一个指定值的所有出现替换为另一个。

泛型

介绍

  • Java泛型是JDK1.5中引入的新特性,其本质是参数化类型,把类型作为参数传递。
  • 可以在类声明时通过一个标识(E,V,T,Q等任意字母皆可)表示类中的某个属性的类型,或者是某个方法的返回值类型,或者是参数类型。在类实例化时(编译期间)指定具体的类型即可。
  • 表示一种数据类型的数据类型。

使用传统方法的问题(以ArrayList为例)

  • 不能对加入到集合ArrayList中的数据类型进行约束,不安全
  • 遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响

使用泛型的好处

  • 编译时,检查添加元素的类型,提高了安全性
  • 减少了类型转换的次数,提高效率
  • 提高代码的重用性
  • 防止类型转换异常,提高代码的安全性

泛型使用细节

  • 标识表示的只能是引用类型。
  • 在指定泛型具体类型后,可以传入该类型或者其子类型
  • 泛型使用形式
    • List<Integer> list1 = new ArrayList<Integer>();
    • List<Integer> list2 = new ArrayList<>();
  • 如果这样写List list3 = new ArrayList(); 默认给它的泛型就是[ E就是Object]
  • 静态方法和静态属性中不能使用泛型(因为在类加载时,对象还没创建,类型不确定,JVM无法初始化)

泛型类

  • 类名

  • T是类型占位符,表示一种引用类型,如果编写多个使用逗号隔开

    public class MyGeneric<T>{}
    
  • 泛型可以作为方法的参数

    T t;
    public void show(T t){
        System.out.println(t);
    }
    
  • 泛型可以作为方法的返回值

    public T getT(){
        return t;
    }
    
  • 在方法中可以创建泛型变量,但不能new泛型对象,因为不清楚T的类型

  • 不同泛型对象不能相互赋值

    MyGeneric<String> ms = new MyGeneric<String>;
    MyGneric<Integer> mi = ms;         //报错
    

泛型接口

  • 接口名

    public interface MyInterface<T>
    
  • 泛型接口的实现类可以是一般类,也可以是泛型类,泛型接口也可以被泛型接口继承

  • 一般类时需指明泛型的类型

    public class MyInterfaceImpl implements MyInterface<Student>
    
  • 泛型实现类的T是什么类型,则泛型接口的T就是什么类型

    public class MyInterfaceImpl<T> implements MyInterface<T>
    
  • 没有指定类型,默认为Object

泛型方法

  • 修饰符 返回值类型 方法名(T t){}

  • 可以定义在泛型类中,也可以定义在普通类中

  • 	public static void main(String[] args) {
            Test test = new Test();
            test.show(52.0);
        }
      	//泛型方法如下
        public <T> void show(T t){//形参这里不写T,调用时无法确定泛型类型
            System.out.println(t.getClass());
        }
    
  • class IA<T> {
        //下面不是泛型方法,而是使用了类声明的泛型的方法
        public void show(T t){}
        //泛型方法的形参列表中可以使用自己声明的泛型,也可以使用类声明的泛型
        public <K> void view(T t , K k){}
    }
    

泛型的继承和通配符

  • 泛型不具备继承性

    List<Object> list = new ArrayList<String>();//错
    List<String> list = new ArrayList<String>();//对
    
  • 通配符

    • <?>:支持任意泛型类型
    • <? extends A>:支持A类以及A类的子类,规定了泛型的上限
    • <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限

线程

相关概念

进程的概念

**进程:**进程是一个可并发执行的、具有独立功能的程序关于某个数据集合的一次执行过程,也是操作系统进行资源分配和保护的基本单位。

该定义表明:进程至少包含程序和数据两个部分。每个进程都有一个进程控制块(Process Control Block , PCB).

什么是线程

​ 在操作系统中,进程代表一个任务单元,如果该任务仅由一个执行流承担,则该进程为单线程进程。如果进程任务被划分为若干并发子任务,分别由不同执行流承担,则改进程称为多线程进程。进程中的每个执行流称为一个线程。线程代表从进程划分出来的更小的任务单元。

每个线程都有一个线程控制块(Task Control Block , TCB).

多线程环境中的进程与线程

​ 多线程进程将任务执行实体与资源分配和拥有实体分离开来,线程属于任务执行实体,进程则作为资源分配和拥有实体,多个线程共享进程拥有的资源。多线程进程将进程间的合作变为进程内多个线程间的合作。

并发性与并行性

并发性是指两个或两个以上的事件或活动在同一时间间隔内发生。已经开始、尚未结束的两个或多个事件或活动是并发的,并发的事件或活动在宏观上是同时进行的,在微观上可能是同时进行的,也可能是交替穿插进行的(CPU多路复用,对系统中的多个进程进行切换)。

并行性是指两个或两个以上的事件或活动在同一时刻发生。并行的事件或活动在宏观和微观上都是同时进行的。

线程基本使用

定义线程的两种方式

1.继承Thread类,重写run()方法

关于线程类:

  • Thread类继承Runnable接口。

  • 当一个类继承了Thread类,该类就可以当作线程使用。程序员会重写run()方法,写上自己的业务代码。

在其他线程中启动子线程步骤:

  • 创建子线程类对象(继承Thread类),可以当作线程使用。

  • 对象调用start()方法,该线程被启动。

  • 子线程类中的run()方法会被自动执行。

为什么要调用start()方法:

  • 如果不调用start()方法,直接调用run()方法,则没有启动新线程,run()方法仍然在原来的线程中执行,这时就相当于一个普通的方法了。

  • 在start()方法的源码中其实调用了start0()方法,start0()是本地方法,由JVM调用,底层是c/c++实现的。

  • 真正实现多线程效果的是start0()方法,不是run()方法。

注意事项:

  • 当main线程启动一个子线程,主线程不会阻塞,会继续执行。main线程结束不代表进程结束。

  • 可以使用jconsole监控线程执行情况(控制台输入)。

  • Thread的类方法sleep(),休眠线程,单位是毫秒ms.

2.实现Runnable接口,重写run()方法

关于Runnable接口:

只有一个run方法

在其他线程中启动子线程步骤:

  • 创建子线程类对象(实现Runnable接口) Dog dog = new Dog();
  • 创建Thread对象,把子线程类对象作为参数传入Thread构造器 Thread thread = new Thread(dog);
  • Thread对象调用start()方法 thread.start(); start()方法调用start0方法,start0()方法把该线程置为可运行状态并在线程开始执行后调用run()方法,run()方法再调用dog的run()方法。

两种方式的区别

  • 从java的设计来看,两种方式本质上没有区别,Thread类也实现了Runnable接口。

  • 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。

    Dog dog = new Dog();
    Thread thread1 = new Thread(dog);
    Thread thread2 = new Thread(dog);
    thread1.start();
    thread2.start();
    //两个线程共享一个dog对象。继承Thread类的方法每创建一个线程,就要创建new一个对象
    

线程终止

  • 线程完成任务后,会自动退出。
  • 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。(例如run()方法中有while(loop)循环,可以在其他线程中该改变loop的布尔值来使死循环结束,从而使run()方法结束,从而使该线程完成任务自动退出。)

线程的属性/方法

    • Modifier and TypeField and Description
      static intMAX_PRIORITY 线程可以拥有的最大优先级。
      static intMIN_PRIORITY 线程可以拥有的最小优先级。
      static intNORM_PRIORITY 分配给线程的默认优先级。
    • Modifier and TypeMethod and Description
      static intactiveCount() 返回当前线程的thread group及其子组中活动线程数的估计。
      voidcheckAccess() 确定当前正在运行的线程是否有权限修改此线程。
      protected Objectclone() 将CloneNotSupportedException作为线程抛出无法有意义地克隆。
      static ThreadcurrentThread() 返回对当前正在执行的线程对象的引用。
      static voiddumpStack() 将当前线程的堆栈跟踪打印到标准错误流。
      static intenumerate(Thread[] tarray) 将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
      static Map<Thread,StackTraceElement[]>getAllStackTraces() 返回所有活动线程的堆栈跟踪图。
      ClassLoadergetContextClassLoader() 返回此Thread的上下文ClassLoader。
      static Thread.UncaughtExceptionHandlergetDefaultUncaughtExceptionHandler() 返回当线程由于未捕获异常突然终止而调用的默认处理程序。
      longgetId() 返回此线程的标识符。
      StringgetName() 返回此线程的名称。
      intgetPriority() 返回此线程的优先级。
      StackTraceElement[]getStackTrace() 返回表示此线程的堆栈转储的堆栈跟踪元素数组。
      Thread.StategetState() 返回此线程的状态。
      ThreadGroupgetThreadGroup() 返回此线程所属的线程组。
      Thread.UncaughtExceptionHandlergetUncaughtExceptionHandler() 返回由于未捕获的异常,此线程突然终止时调用的处理程序。
      static booleanholdsLock(Object obj) 返回 true当且仅当当前线程在指定的对象上保持监视器锁。
      voidinterrupt() 中断这个线程。
      static booleaninterrupted() 测试当前线程是否中断。
      booleanisAlive() 测试这个线程是否活着。
      booleanisDaemon() 测试这个线程是否是守护线程。
      booleanisInterrupted() 测试这个线程是否被中断。
      voidjoin() 等待这个线程死亡。(线程插队,若成功,则执行完毕再执行其他线程)
      voidjoin(long millis) 等待这个线程死亡最多 millis毫秒。
      voidjoin(long millis, int nanos) 等待最多 millis毫秒加上 nanos纳秒这个线程死亡。
      voidrun() 如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
      voidsetContextClassLoader(ClassLoader cl) 设置此线程的上下文ClassLoader。
      voidsetDaemon(boolean on) 将此线程标记为守护线程或用户线程。
      static voidsetDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 设置当线程由于未捕获的异常突然终止而调用的默认处理程序,并且没有为该线程定义其他处理程序。
      voidsetName(String name) 将此线程的名称更改为等于参数 name
      voidsetPriority(int newPriority) 更改此线程的优先级。
      voidsetUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 设置当该线程由于未捕获的异常而突然终止时调用的处理程序。
      static voidsleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
      static voidsleep(long millis, int nanos) 导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。
      voidstart() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
      StringtoString() 返回此线程的字符串表示,包括线程的名称,优先级和线程组。
      static voidyield() 对调度程序的一个暗示,即当前线程愿意让出当前使用的处理器。可能不成功

守护线程

  • 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  • 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  • 常见的守护线程:垃圾回收机制
  • setDaemon(boolean on) 将此线程标记为守护线程或用户线程。然后再start()启动线程

线程的生命周期

线程七大状态

NEW 线程尚未启动的线程状态。
RUNNABLE 可运行线程的线程状态。 可分为:就绪态(Ready)和运行态(Running)
WAITING 等待线程的线程状态
TIMED_WAITING 具有指定等待时间的等待线程的线程状态。
BLOCKED 一个线程的线程状态阻塞,等待监视器锁定。
TERMINATED 终止线程的线程状态。

线程同步

保证数据在任一时刻,最多有一个线程访问。

同步具体方法

同步代码块
synchronized(对象){//得到对象的锁,才能操作代码。称为对象互斥锁
    //需要同步的代码
}
//对象可以是this,也可以是其他对象,但必须是同一个
//若同步代码块在静态方法中,括号里不能写this或其他对象,应该为(当前类.class)
同步方法
public synchronized void method(参数列表){
    //需要同步的代码
    
}
//这种方式锁在this对象
//若是静态方法,锁在类本身

线程的死锁

定义

在一个线程集合中的每个线程都在等待只能由该集合中的其他线程才能引发的事件,而无限期陷入僵持的局面

释放锁的情况

  • 当前线程的同步方法、同步代码块执行结束。
  • 当前线程在同步方法、同步代码块中遇到break/return。
  • 当前线程在同步方法、同步代码块中出现了未处理的Error或Exception,导致线程异常结束。
  • 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

不会释放锁的情况

  • 当前线程执行同步方法、同步代码块时,程序调用了Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。
  • 当前线程在同步方法、同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。该方法不推荐使用。

I/O

概念

内存与存储设备之间传输数据的通道

分类

按方向
  • 输入流:将<存储设备>中的内容读入<内存>中
  • 输出流:将<内存>中的内容写入<存储设备>中
按单位
  • 字节流:以字节为单位,可以读写所有数据
  • 字符流:以字符为单位,只能读写文本数据
按功能
  • 节点流:具有实际传输数据的读写功能,可以从一个特定的数据源读写数据
  • 处理流(包装流):连接在已存在的流之上,在节点流的基础之上增强功能

节点流和处理流

区别和联系
  • 节点流是底层流,直接跟数据源相接
  • 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
  • 处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
处理流的作用
  • 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便

字节流

InputStream抽象类

    • intavailable() 返回从该输入流中可以读取(或跳过)的字节数的估计值,而不会被下一次调用此输入流的方法阻塞。
      voidclose() 关闭此输入流并释放与流相关联的任何系统资源。
      voidmark(int readlimit) 标记此输入流中的当前位置。
      booleanmarkSupported() 测试这个输入流是否支持 markreset方法。
      abstract intread() 从输入流读取数据的下一个字节。
      intread(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b
      intread(byte[] b, int off, int len) 从输入流读取最多 len字节的数据到一个字节数组。
      voidreset() 将此流重新定位到上次在此输入流上调用 mark方法时的位置。
      longskip(long n) 跳过并丢弃来自此输入流的 n字节数据。
FileInputStream
public void FileInputStream_(){
        FileInputStream fis = null;
        byte[] buf = new byte[8];
        int res = 0;
        try {
            fis = new FileInputStream("d://hello.txt");
            while ((res = fis.read(buf)) != -1){//一次读取8个字节并放到buf中,返回读取字节数量,读完返回-1
                System.out.print(new String(buf,0,res));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
BufferedInputStream处理流
//文件流就像水管,缓冲流就像水缸
        FileInputStream fis = new FileInputStream("d:\\test.txt");
        BufferedInputStream bos = new BufferedInputStream(fis);
//构造器BufferedInputStream(InputStream)
        int count = 0;
//        while((count= bos.read())!=-1){
//            System.out.print((char)count);
//        }
        byte[] buf = new byte[1024];
        while((count=bos.read(buf))!=-1){
            System.out.println(new String(buf,0,count));
        }
        bos.close();//fis自动关闭
ObjectInputStream处理流

使用ObjectInputStream从硬盘中读取对象的过程称为反序列化

OutputStream抽象类

    • voidclose() 关闭此输出流并释放与此流相关联的任何系统资源。
      voidflush() 刷新此输出流并强制任何缓冲的输出字节被写出。
      voidwrite(byte[] b)b.length字节从指定的字节数组写入此输出流。
      voidwrite(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
      abstract voidwrite(int b) 将指定的字节写入此输出流。
FileOutputStream
    public void FileOutStream_(){
        String path = "d://demo.txt";
        FileOutputStream fos = null;
        try {
            //带有布尔参数且值为true的构造器,写入文件的内容会追加到原内容之后
            //布尔参数值为false或不带布尔参数的构造器,写入文件的内容会覆盖原内容
            //此处所说的原内容不是每一次write之前的原内容,而是每一次获取该文件的访问资源
            fos = new FileOutputStream(path,true);
            String str = "4amgodv";
            //若文件不存在,会先创建文件
            fos.write(str.getBytes(),3,4);//下标3,长度4,godv
            //下面的内容不会覆盖上面的"godv"
            fos.write(str.getBytes(),0,4);//4amg
            fos.write(str.getBytes(),0,5);//4amgo
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
BufferedOutputStream处理流
FileOutputStream fos = new FileOutputStream("d:\\test.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
for (int i = 0; i < 100; i++) {
    bos.write("hfaisdbfaibdfia".getBytes());
    //bos.flush();//将缓存流中的数据刷新到硬盘中
}
bos.close();//关闭流时bos会调用flush()
ObjectOutputStream处理流

使用ObjectOutputStream将对象写入到硬盘中的过程称为序列化

PrintStream
  • 继承FilterOutStream,FilterOutStream继承OutPutStream

  • 默认情况下,PrintStream输出数据的位置是 标准输出,即显示器

  • 输出方法:print(形参),参数可以是基本数据类型和String; //底层也是调用write()方法

    ​ write(形参),参数是字节数组Byte[];

  • 修改打印流输出位置:System.setOut(PrintStream ps),PrintStream的构造器可以传入文件路径。

    System.setOut(new PrintStream("d:\\hello.txt"));
    System.out.println("输出到哪里了");//输出到d:\\hello.txt
    

字节流复制文件

    public void copy(){
        String sourceFilePath = "d://panda.jpg";
        String desFilePath = "d://xiongmao.jpg";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        byte[] buf = new byte[1024];
        int count = 0;
        try {
            fileInputStream = new FileInputStream(sourceFilePath);
            fileOutputStream = new FileOutputStream(desFilePath);
            while((count = fileInputStream.read(buf)) != -1){
                fileOutputStream.write(buf,0,count);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

序列化与反序列化

序列化对象类型
public class Student implements Serializable {
    private static final long serialVersionUID = 100L;
    private String name;
    private int age;

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
序列化

定义:在保存数据时,保存数据的值和数据类型,若要序列化则ObjectOutputStream对象不能使用write()来保存

 public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream("d:\\student.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        Student s1 = new Student("张三",20);
        oos.writeObject(s1);
        oos.close();
    }
反序列化

定义:就是在恢复数据时,恢复数据的值和数据类型

public static void main(String[] args) throws Exception{
        FileInputStream fis = new FileInputStream("d:\\student.bin");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Student s = (Student)ois.readObject();//需要注意Student类的访问权限
        System.out.println(s.toString());
        ois.close();
    }
注意事项
  • 读写顺序要一致

  • 序列化类必须要实现Serializable接口(标记接口,没有方法)或者Externalizable(继承了Serializable接口,有方法,一般不使用)

  • 序列化类中成员属性的类型要求实现Serializable接口

  • 序列化版本号ID,serialVersionUID,保证序列化的类和反序列化的类是同一个类

  • 使用transient(瞬时的)修饰属性,则该属性不能被序列化

  • 静态属性不能序列化

  • 序列化多个对象,可借助集合实现

  • 序列化具备可继承性

标准输入输出流

编译类型运行类型默认设备
System.in标准输入InputStreamBufferedInputStream键盘
System.out标准输出PrintStreamPrintStream显示器

字符流

Reader抽象类

FileReader

将"为人民服务"以utf-8编码成01数据流存储到GBK文件中,则显示中文乱码,但调用FileReader.read()方法读取一个字符的Unocode编码,读到的仍是"为人民服务"的Unicode编码,而不是所显示中文乱码的Unocode编码,且该GBK文件自动转换为utf-8文件,中文乱码转换为"为人民服务"。

public static void main(String[] args) throws Exception{
        FileReader fr = new FileReader("d:\\info.txt");
        int count = 0;
        while((count=fr.read())!=-1){
            System.out.print((char)count); 
            //count保存读到的一个字符单位的Unicode编码,不一定是文本所显示的字符对应的Unicode编码
        }
        fr.close();
    }
    public void FileReader_(){
        String path = "d://story.txt";
        FileReader fileReader = null;
        char[] buf = new char[16];
        int readLen = 0;
        try {
            fileReader = new FileReader(path);
            while((readLen = fileReader.read(buf)) != -1){
                System.out.print(new String(buf, 0, readLen));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileReader != null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
BufferedReader(包装流)
public class BufferedReaderTest {
    public static void main(String[] args) throws Exception{
        FileReader fr = new FileReader("d:\\test.txt");
        BufferedReader br = new BufferedReader(fr);
//        int count = 0;
//        char[] buf = new char[1024];
//        while((count=br.read(buf))!=-1){
//            System.out.println(new String(buf,0,count));
//        }
        String line = null;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }
        br.close();//只关闭处理流即可
    }
}

Writer抽象类

FileWriter
    public void FileWrter_(){
        String path = "d://a.txt";
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(path);   //构造器也分为覆盖模式和追加模式
            fileWriter.write('G');              //write(int)
            String str = "好好学习,天天向上";
            fileWriter.write(str);             //write(String)
            fileWriter.write(str.toCharArray());       //write(char[])
            fileWriter.write(str,0,5);             //好好学习,write(String, int off, int len)
            fileWriter.write(str.toCharArray(),5,4);           //天天向上,  write(char[], int off, int len)
           //数据量大的时候,可以使用循环
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileWriter != null){
                try {
                    fileWriter.close();     //一定要关闭或者flush,数据才能写入文件
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
BufferedWriter(包装流)
ublic class BufferedWriterTest {
    public static void main(String[] args) throws Exception{
        FileWriter fw = new FileWriter("d:\\test.txt");
        //如果需要设置成追加模式,那么在节点流的构造器中设置成追加模式即可
        BufferedWriter bw = new BufferedWriter(fw);
        for (int i = 0; i < 10; i++) {
            bw.write("锄禾日当午,汗滴禾下土");
            bw.newLine();//插入一个换行符,不同系统的换行符不同,使用newLine可移植性更好
            bw.flush();
        }
        bw.close();//只需关闭外层流即可,内层流会自动关闭
    }
}
PrintWriter
  • 继承Writer
public static void main(String[] args) throws Exception{
        PrintWriter pw = new PrintWriter("d:\\test.txt");
    	//PrintWriter pw = new PrintWriter(new FileWriter("d:\\test.txt"));
        pw.println(97);
        pw.println("97");
        pw.println("\\\"97\\\"");
        pw.println('a');
        pw.close();
        /*
        97
        97
        \"97\"
        a
         */
    }

字符流复制文件

只能复制文本文件等有字符编码的文件,不能复制图片和二进制文件,其他使用方法类比字节流复制文件

转换流

默认情况下按照utf-8编码读取

InputStreamReader
public static void main(String[] args) throws Exception{
        FileInputStream fis = new FileInputStream("d:\\test.txt");
        InputStreamReader isr = new InputStreamReader(fis,"utf-8");//可以指定编码格式
        int count = 0;
        while((count=isr.read())!=-1){
            System.out.print((char)count);//根据字符类型,一次读取一个或多个字节
        }
        isr.close();
    }
OutputStreamWriter
public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream("d:\\ex.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
        for (int i = 0; i < 10; i++) {
            osw.write("一二三四");
            osw.flush();
        }
        osw.close();
    }

File类

概念

代表物理盘符中的一个文件或者文件夹

细节

2个\是因为第一个是转义符

/作为分隔也可以

方法

  • 构造器
new File(String psthname);//根据路径构建一个File对象
new File(File parent, String child);//根据父目录文件 + 子路径构建
new File(String parent, String child);//根据父目录路径 + 子路径构建
File file = new File("d://test1.txt");//在内存中创建File对象
file.createNewFile();//在磁盘中创建test1.txt文件
File parentFile = new File("d://");
File file = new File(parentFile, "test2.txt");
file.cerateNewFile();
File file = new File("d://", "test3.txt");
file.cerateNewFile();
  • 获取文件信息的方法
file.getAbsolutePath();      //获取文件的绝对路径
file.getPath();     //获取路径
file.getName();   //获取文件名称
file.getParent();   //获取父目录
file.length();     //获取文件长度,以字节为单位
file.lastModified();   //获取文件最后修改时间,返回值类型为long
dir....
  • 判断方法
file.exists();   //文件是否存在
file.canWrite();  //是否可写
file.isFile();   //是否是文件
dir.isDirectory();  //是否是文件夹
file.isHidden();  //是否隐藏
  • 分隔符
   static pathSeparator()  //路径分隔符
   static separator()    //名称分隔符
  • 文件删除
file.delete();    //直接删除,返回布尔值
file.deleteOnExit();    //jvm退出时删除
  • 文件夹操作
File dir = new File("d:\\aaa\\bbb\\ccc");   //创建文件夹对象
dir.mkdir();//创建单级目录(在磁盘中)
dir.mkdirs();//创建多级目录(在磁盘中)
dir.delete();//删除文件夹,该文件夹需为空
dir.deleteOnExit();
  • 遍历文件夹
  dir.list()   //返回字符串数组
  dir.listFiles()  //返回File数组

FileFilter

  • 文件过滤器
File[] fileList = dir.listFiles(new FileFilter(){
      @override
      public boolean accept(File pathname){
          //符合条件,返回true
          //不符合条件,返回false
      }
  });  

java绘图坐标体系

坐标体系介绍

  • X轴水平方向,右正;Y轴垂直,下正。

  • 正正区间,原点位于左上角。

  • 单位是像素点。

像素

分辨率800 x 600代表屏幕上的每一行有800个像素点组成,共有600行。

像素是密度单位。

ublic class BufferedWriterTest {
public static void main(String[] args) throws Exception{
FileWriter fw = new FileWriter(“d:\test.txt”);
//如果需要设置成追加模式,那么在节点流的构造器中设置成追加模式即可
BufferedWriter bw = new BufferedWriter(fw);
for (int i = 0; i < 10; i++) {
bw.write(“锄禾日当午,汗滴禾下土”);
bw.newLine();//插入一个换行符,不同系统的换行符不同,使用newLine可移植性更好
bw.flush();
}
bw.close();//只需关闭外层流即可,内层流会自动关闭
}
}


#### PrintWriter

- 继承Writer

```java
public static void main(String[] args) throws Exception{
        PrintWriter pw = new PrintWriter("d:\\test.txt");
    	//PrintWriter pw = new PrintWriter(new FileWriter("d:\\test.txt"));
        pw.println(97);
        pw.println("97");
        pw.println("\\\"97\\\"");
        pw.println('a');
        pw.close();
        /*
        97
        97
        \"97\"
        a
         */
    }

字符流复制文件

只能复制文本文件等有字符编码的文件,不能复制图片和二进制文件,其他使用方法类比字节流复制文件

转换流

默认情况下按照utf-8编码读取

InputStreamReader
public static void main(String[] args) throws Exception{
        FileInputStream fis = new FileInputStream("d:\\test.txt");
        InputStreamReader isr = new InputStreamReader(fis,"utf-8");//可以指定编码格式
        int count = 0;
        while((count=isr.read())!=-1){
            System.out.print((char)count);//根据字符类型,一次读取一个或多个字节
        }
        isr.close();
    }
OutputStreamWriter
public static void main(String[] args) throws Exception{
        FileOutputStream fos = new FileOutputStream("d:\\ex.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
        for (int i = 0; i < 10; i++) {
            osw.write("一二三四");
            osw.flush();
        }
        osw.close();
    }

File类

概念

代表物理盘符中的一个文件或者文件夹

细节

2个\是因为第一个是转义符

/作为分隔也可以

方法

  • 构造器
new File(String psthname);//根据路径构建一个File对象
new File(File parent, String child);//根据父目录文件 + 子路径构建
new File(String parent, String child);//根据父目录路径 + 子路径构建
File file = new File("d://test1.txt");//在内存中创建File对象
file.createNewFile();//在磁盘中创建test1.txt文件
File parentFile = new File("d://");
File file = new File(parentFile, "test2.txt");
file.cerateNewFile();
File file = new File("d://", "test3.txt");
file.cerateNewFile();
  • 获取文件信息的方法
file.getAbsolutePath();      //获取文件的绝对路径
file.getPath();     //获取路径
file.getName();   //获取文件名称
file.getParent();   //获取父目录
file.length();     //获取文件长度,以字节为单位
file.lastModified();   //获取文件最后修改时间,返回值类型为long
dir....
  • 判断方法
file.exists();   //文件是否存在
file.canWrite();  //是否可写
file.isFile();   //是否是文件
dir.isDirectory();  //是否是文件夹
file.isHidden();  //是否隐藏
  • 分隔符
   static pathSeparator()  //路径分隔符
   static separator()    //名称分隔符
  • 文件删除
file.delete();    //直接删除,返回布尔值
file.deleteOnExit();    //jvm退出时删除
  • 文件夹操作
File dir = new File("d:\\aaa\\bbb\\ccc");   //创建文件夹对象
dir.mkdir();//创建单级目录(在磁盘中)
dir.mkdirs();//创建多级目录(在磁盘中)
dir.delete();//删除文件夹,该文件夹需为空
dir.deleteOnExit();
  • 遍历文件夹
  dir.list()   //返回字符串数组
  dir.listFiles()  //返回File数组

FileFilter

  • 文件过滤器
File[] fileList = dir.listFiles(new FileFilter(){
      @override
      public boolean accept(File pathname){
          //符合条件,返回true
          //不符合条件,返回false
      }
  });  

java绘图坐标体系

坐标体系介绍

  • X轴水平方向,右正;Y轴垂直,下正。

  • 正正区间,原点位于左上角。

  • 单位是像素点。

像素

分辨率800 x 600代表屏幕上的每一行有800个像素点组成,共有600行。

像素是密度单位。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值