JAVA基础学习

Hello World

  1. 随便建一个文件夹,存放代码

  2. 新建一个java文件,文件后缀名为.java

    • Hello.java

  3. 编写代码

public class Hello{
    public static void main(String[] args){
        System.out.print("Hello World!");
    }
}
  1. cmd编译javac java文件,会生成一个class文件,例如:javac Hello.java

  2. 运行class文件,java class文件,例如:java Hello

可能遇到的问题

  1. 每个单词的大小写不能出现问题,java是大小写敏感的

  2. 尽量使用英文;

  3. 文件名和类名必须保证一致,并且首字母大写

  4. 符号使用了中文

IDEA安装

  1. jetbrains网站

  2. 找到developer tools选中IDEA

  3. 下载Community免费版

  4.  

  5. 一直下一步

JAVA基础语法

注释

  1. 单行注释://

  2. 多行注释:/**/

  3. 文档注释:

/**
*我是文档注释
*/
  1. 选中代码ctrl+/

Java标识符命名

  1. 首字母只能是大小写字母以及美元符$或者下划线_开始

数据类型

  1. 强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用(安全)

  2. 弱类型语言:与强类型语言相反

  3. 数据类型:

    • 八大基本类型:byte 1字节,short 2字节,int 4字节,long 8字节,float 4字节,double 8字节,char 2字节,boolean

    • 引用类型:类,接口,数组

  4. 整数扩展:进制 二进制前面+0b 十进制 八进制前面+0 十六进制前面+0x

  5. 浮点数拓展?银行业务怎么表示?钱?
    //BigDecimal 数学工具类
    //最好完全避免使用浮点数进行比较
  6. //所有的字符本质还是数字
  7. boolean flag=true;
            if(flag==true){}          两个相等
            if(flag){}

类型转换

  1. 从低到高:byte-->short-->char-->int-->long-->float-->double

  2. //强制转换 (类型)变量名 高-->低
    /*自动转换  低-->高 例:
    int i=128;
    double d=i;
    */
  3. /*
    注意点:
    1.不能对布尔值进行转换
    2.不能把对象类型转换为不相干的类型
    3.在把高容量转换到低容量的时候,强制转换
    4.转换的时候可能存在内存溢出,或者精度问题!
     */

变量

  1. 类变量:在类下,static修饰

  2. 局部变量:方法里,必须声明和初始化

  3. 实例变量:在类下,从属于对象,如果不自行初始化,为这个类型的默认值

  4. 布尔值默认是false

  5. 除了基本类型,其余的默认值都是null

变量的命名规范

  1. 变量与方法名:首字母小写和驼峰原则:monthSalary

  2. 类名:首字母大写和驼峰原则:GoodMan

  3. 常量名一般用大写字符。

常量

  1. final修饰,值被设定后,在程序运行过程中不允许被改变。

  2. 常量名一般用大写字符。

  3. final修饰的类不可以被继承。

  4. final修饰的方法不可以重写。

运算符

  1.  

  2. public static void main(String[] args) {
        long a=1212112112L;
        int b=123;
        short c=10;
        byte d=8;
        System.out.println(a+b+c+d);//long
        System.out.println(b+c+d);//int
        System.out.println(c+d);//int
    }
  3. public static void main(String[] args) {
            //关系运算符返回的结果:正确,错误  布尔值
            int a=10;
            int b=20;
            int c=21;
            //取余,模运算
            System.out.println(c%a);//c/a  21/10=2...1
            System.out.println(a>b);//false
            System.out.println(a<b);//true
            System.out.println(a==b);//false
            System.out.println(a!=b);//true
        }
  4. //二元运算符
    public static void main(String[] args) {
        //二元运算符
        //Ctrl+D:复制当前行到下一行
        int a=10;
        int b=20;
        int c=30;
        int d=40;
        System.out.println(a+b);
        System.out.println(a-b);
        System.out.println(a*b);
        System.out.println(a/(double)b);
    
    }
  5. //一元运算符
       public static void main(String[] args) {
               //++自增    都是一元运算符
               //--自减
               int a=3;
               int b=a++;//a++执行完这行代码后,先给b赋值,再自增
               System.out.println(a);//4
               int c=++a;//++a执行完这行代码前,先自增,再给b赋值
               System.out.println(a);//5
               System.out.println(b);//3
               System.out.println(c);//5
               //幂运算2^3  2*2*2=8
               double pow=Math.pow(2,3);
               System.out.println(pow);
           }
  6. //逻辑运算符
    public static void main(String[] args) {
            //与(and) 或(or) 非(取反)
            boolean a=true;
            boolean b=false;
            System.out.println("a && b:"+(a && b));//逻辑与运算,两个变量为真,结果才为true
            System.out.println("a || b:"+(a || b));//逻辑或运算,两个变量有一个为真,结果就为true
            System.out.println("!(a && b):"+!(a && b));//如果是真,则变为假,如果是假就变为真
            
            //短路运算
            int c=5;
            boolean d=(c<4)&&(c++<4);
            System.out.println(d);//false
            System.out.println(c);//c=5
        }
  7. //位运算
    public static void main(String[] args) {
        /*位运算
        A=0011 1100
        B=0000 1101
        A&B    0000 1100
        A|B    0011 1101
        A^B    0011 0001
        ~B     1111 0010
    
        2*8=16  2*2*2*2
        效率极高!!!
        <<  *2
        >>  /2
        0000 0000     0
        0000 0001     1
        0000 0010     2
        0000 0011     3
        0000 0100     4
        0000 1000     8
        0001 0000     16
         */
        System.out.println(2<<3);
    }
  8. public static void main(String[] args) {
        int a=10;
        int b=20;
        a+=b;//a=a+b;
        //a-=b;//a=a-b;
        System.out.println(a);//30
        //字符串连接符   +   ,String
        System.out.println(a+b);
        System.out.println(""+a+b);//3020
        System.out.println(a+b+"");//50
    }

包机制

  1. 一般利用公司域名倒置作为包名:com.baidu.www

JavaDoc

  1. @author 作者名

  2. @version版本名

  3. @since指名需要最早使用的jdk版本

  4. @param参数名

  5. @return返回值情况

  6. @throws异常抛出情况

  7. 进入命令提示符=>找到HelloWorld.java文件=>javadoc -encoding UTF-8 -charset UTF-8 HelloWorld.java

Java流程控制

Scanner

public static void main(String[] args) {
    //输入多个数字,并求其总和与平均数,每输入一个数字用回车确认,通过输入非数字来结束输入并输出执行结果。
    Scanner scanner=new Scanner(System.in);
    //和
    double sum=0;
    //计算输入了多少个数字
    int m=0;
    //通过循环判断是否还有输入,并在里面通过每一次进行求和统计
    while (scanner.hasNextDouble()){
        double x=scanner.nextDouble();
        m=m+1;//m++
        sum=sum+x;
        System.out.println("你输入了第"+m+"个数据,然后当前结果sum="+sum);
    }
    System.out.println(m+"个数的和为"+sum);
    System.out.println(m+"个数的平均值是"+(sum/m));
    scanner.close();
}

顺序结构

public static void main(String[] args) {
    System.out.println("hello1");
    System.out.println("hello2");
    System.out.println("hello3");
    System.out.println("hello4");
    System.out.println("hello5");
    System.out.println("hello6");
}

选择结构

  1. //if-else if-else
    public static void main(String[] args) {
        //考试分数大于60就是及格,小于60分就不及格
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入成绩:");
        int score=scanner.nextInt();
        if(score==100){
            System.out.println("恭喜满分");
        }else if(score<100&&score>=90){
            System.out.println("A级");
        }else if(score<90&&score>=80){
            System.out.println("B级");
        }else if(score<80&&score>=70){
            System.out.println("C级");
        }else if(score<70&&score>=60){
            System.out.println("D级");
        }else if(score<60&&score>=0){
            System.out.println("不及格");
        }else{
            System.out.println("成绩不合法!");
        }
        scanner.close();
    }
  2. switch case语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。

    • byte,short,int,char,String

    • case标签必须为字符串常量或字面量。

//switch
public static void main(String[] args) {
    String name="GX";

    //反编译 java---class(字节码文件)----反编译(IDEA)
    switch (name){
        case "jj":
            System.out.println("jj");
            break;
        case "GX":
            System.out.println("GX");
            break;
        default:
            System.out.println("弄啥捏!");
    }
}

While循环

//while 1到100的和
public static void main(String[] args) {
    int i=0;
    int sum=0;
    while(i<=100){
        sum=sum+i;
        i++;
    }
    System.out.println(sum);
}

do...while

  1. 循环体至少会执行一次

  2. //do...while
    public static void main(String[] args) {
        int a=0;
        while (a<0){
            System.out.println(a);
            a++;
        }//没有输出
        System.out.println("=================");
        do {
            System.out.println(a);
            a++;
        }while (a<0);//0
    }

for循环

  1. 是最有效、最灵活的循环结构

  2. //初始化//条件判断//迭代
    for(int i=1;i<=100;i++){
        System.out.println(i);
    }
    System.out.println("for循环结束!");
    //死循环
    for(;;){
        
    }
  3. //练习1:计算0到100之间的奇数和,偶数的和
    public static void main(String[] args) {
        int oddSum=0;
        int evenSum=0;
        for (int i = 0; i <=100; i++) {
            if(i%2!=0){
                oddSum+=i;
            }else{
                evenSum+=i;
            }
        }
        System.out.println("奇数的和:"+oddSum);
        System.out.println("偶数的和:"+evenSum);
    }
  4. //练习2:用while或for循环输出1-1000之间能被5整除的数,并且每行输出3个
    //        for (int i = 1; i <=1000; i++) {
    //            if(i%5==0){
    //                System.out.print(i+"\t");
    //            }
    //            if(i%(5*3)==0){//换行
    //                System.out.println();
    //                //System.out.print("\n");
    //            }
    //        }
            //println 输出完会换行
            //print  输出完不会换行
            int i=1;
            while(i<=1000){
                if(i%5==0){
                    System.out.print(i+"\t");
                }
                if(i%(5*3)==0){
                    System.out.print("\n");
                }
                i++;
            }
        }
  5. //练习3 九九乘法表
    public static void main(String[] args) {
        //九行 第一个数 每行的开头都是行数 例如:3行 3*1 3*2 3*3
        //第二个数 1到行数 例如第三行 1 2 3
        for(int i=1;i<=9;i++){
            for(int j=1;j<=i;j++){
                System.out.print(i+"*"+j+"="+(i*j)+"\t");
            }
            System.out.println();
        }
    }

增强for循环

public static void main(String[] args) {
    int[] numbers={10,20,30,40,50};//定义一个数组
    //遍历数组的元素
    for(int x:numbers){
        System.out.println(x);
    }
}

break和continue

  1. break和continue都是用来控制循环结构的,主要作用是停止循环。

  2. break用于跳出一个循环体或者完全结束一个循环,不仅可以结束其所在的循环,还可结束其外层循环。

  3. 只能在循环体内和switch语句体内使用break。

  4. 不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循环,开始执行循环之后的代码。

  5. 当break出现在循环体中的switch语句体内时,起作用只是跳出该switch语句体并不能终止循环体的执行。若想强行终止循环体的执行,可以在循环体中,但并不在switch语句中设置break语句,满足某种条件则跳出本层循环体。

  6. continue语句的作用是跳过本次循环体中剩下尚未执行的语句,立即进行下一次的循环条件判定,可以理解为只是中止(跳过)本次循环,接着开始下一次循环。

  7. continue语句并没有使整个循环终止。

  8. continue 只能在循环语句中使用,即只能在 for、while 和 do…while 语句中使用。

  9. goto关键字,"标签"是指后面跟一个冒号的标识符,例如:label:

    public static void main(String[] args) {
        //打印101-150之间所有的质数
        //质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数0
        int count=0;
        //不建议使用
        outer:for(int i=101;i<=150;i++){
            for(int j=2;j<=i/2;j++){
                if(i%j==0){
                    continue outer;
                }
            }
            System.out.println(i);
        }
    }

练习打印三角形

//打印三角形
public static void main(String[] args) {
    //5行
    for (int i = 1; i <=5; i++) {
        for (int j = 5; j >=i; j--) {
            System.out.print(" ");
        }
        for (int j = 1; j <=i ; j++) {
            System.out.print("*");
        }
        for (int j = 1; j <i; j++) {
            System.out.print("*");
        }
        System.out.println();
    }
}

方法

  1. return终止方法

  2. public static void main(String[] args) {
        int max=max(10,10);
        System.out.println(max);
    }
    //比大小
    public static int max(int num1,int num2){
        int result=0;
        if(num1==num2){
            System.out.println("num1==num2");
            return 0;//return终止方法
        }
        if(num1>num2){
            result=num1;
        }else{
                result=num2;
        }
        return result;
    }
  3. 值传递(pass by value):在调用函数时,将实际参数复制一份传递到函数中,这样在函数中对参数进行修改,就不会影响到原来的实际参数;

    引用传递(pass by reference):在调用函数时,将实际参数的地址直接传递到函数中。这样在函数中对参数进行的修改,就会影响到实际参数;

方法的重载

  1. 在同一个类中,方法名必须相同,参数列表不同(个数不同,类型不同,参数排列顺序不同等)。

  2. 方法的返回值可以相同也可以不相同。

  3. 仅仅返回类型不同不足以成为方法的重载。

命令行传参

public static void main(String[] args) {
    for (int i = 0; i <args.length; i++) {
        System.out.println("args["+i+"]:"+args[i]);
    }
}

找到.java文件的文件夹进入cmd,javac Demo01.java

要退到src目录下 java com.gx.method.Demo01 this is gx

可变参数

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

  2. 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通参数必须在它之前声明。

  3. public static void main(String[] args) {
        printMax(0,34,3,3,2,56.5);
        printMax(0,new double[]{1,2,3});
    }
    public static void printMax(int x,double...numbers){
       if(numbers.length==0){
           System.out.println("No argument passed");
           return;//结束方法
       }
       double max=numbers[0];
       //最大值
        for(int i=1;i<numbers.length;i++){
            if(numbers[i]>max){
                max=numbers[i];
            }
        }
        System.out.println(x);
        System.out.println("The max value is:"+max);
    }

递归

  1. 理解:A方法调用A方法,就是自己调用自己。

  2. 包括两个部分:递归头:什么时候不调用自身方法。如果没有头,将会陷入死循环。

    递归体:什么时候需要调用自身方法。

  3. //2! 2*1
    //3! 3*2*1
    public static void main(String[] args) {
        System.out.println(f(3));//6
    }
    //2 2*f(1) 2*1
    //3 3*f(2) 3*2*f(1) 3*2*1
    public static int f(int n){
        if(n==1){
            return 1;
        }else{
            return n*f(n-1);
        }
    }
  4. 如果调用次数过多会影响机器性能

控制台计算器

//写一个计算器,实现加减乘除功能,并且能够循环的接收新的数据,通过用户交互实现。
    public static void main(String[] args) {
        while (true){
            Scanner scanner=new Scanner(System.in);
            System.out.println("请输入运算符:");
            while (scanner.hasNext()){
                String c=scanner.next();
                System.out.println("请输入两个数a,b:");
                while (scanner.hasNextDouble()){
                    double a=scanner.nextDouble();
                    double b=scanner.nextDouble();
                    switch (c){
                        case "+":
                            System.out.println("a+b为:"+add(a,b));
                            System.out.println("请输入运算符:");
                            break;
                        case "-":
                            System.out.println("a-b为:"+sub(a,b));
                            System.out.println("请输入运算符:");
                            break;
                        case "*":
                            System.out.println("a*b为:"+mul(a,b));
                            System.out.println("请输入运算符:");
                            break;
                        case "/":
                            System.out.println("a/b为:"+div(a,b));
                            System.out.println("请输入运算符:");
                            break;
                        default:
                            System.out.println("运算符或数字错误!");
                            System.out.println("请输入运算符:");
                    }
                }
            }
            scanner.close();
        }
    }
    public static double add(double a,double b){
        return a+b;
    }
    public static double sub(double a,double b){
        return a-b;
    }
    public static double mul(double a,double b){
        return a*b;
    }
    public static double div(double a,double b){
        return a/b;
    }

数组

  1. 数组是相同类型数据的有序集合。

  2. 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。如果越界,则报:ArrayIndexOutofBounds

  3. 数组也是对象。数组元素相当与对象的成员变量。

  4. //变量的类型  变量的名字=变量的值;
        //数组类型
        public static void main(String[] args) {
            int[]nums;//1.声明一个数组
            int num2[];
            nums=new int[2];//创建一个数组
            //给数组元素中赋值
            nums[0]=1;
            nums[1]=2;
            System.out.println(nums[0]);
            //计算所有元素的和
            int sum=0;
            for (int i = 0; i <nums.length ; i++) {
                sum+=nums[i];
            }
            System.out.println("总和为:"+sum);
        }

内存分析

  1. 存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用。

  2. 存放基本变量类型(会包含这个基本类型的具体数值),

    存放引用对象的变量(会存放这个引用对象在堆里面的具体地址)。

  3. 方法区可以被所有的线程共享,包含了所有class和static变量。

  4.  

数组的三种初始化

public static void main(String[] args) {
    //静态初始化:创建+赋值
    int[]a={1,2,3,4,5};
    System.out.println(a[0]);//1
    //动态初始化:包含默认初始化
    int[]b=new int[10];
    b[0]=10;
    System.out.println(b[0]);//10
    System.out.println(b[1]);//0  默认初始化
    System.out.println(b[2]);//0  默认初始化
}

数组的使用

public static void main(String[] args) {
        int[] arrays={1,2,3,4,5};
        for (int array:arrays){
            System.out.println(array);//12345
        }
        printArray(arrays);//12345
        int[]reverse=reverse(arrays);
        printArray(reverse);//54321
    }
    //打印数组元素
    public static void printArray(int[] arrays){
        for (int i = 0; i <arrays.length; i++) {
            System.out.print(arrays[i]+" ");
        }
    }
    //反转数组
    public static int[] reverse(int[] arrays){
        int[] result=new int[arrays.length];
        for (int i =0,j=arrays.length-1;i<arrays.length&&j>=0;i++,j--) {
            result[j]=arrays[i];
        }
        return result;
    }

多维数组

public static void main(String[] args) {
    //[4][2]
    /*
    1,2  array[0]
    2,3  array[1]
    3,4  array[2]
    4,5  array[3]
     */
    int[][] array={{1,2},{2,3},{3,4},{4,5}};
    System.out.println(array[0]);//打印堆中的地址
    printArray(array[0]);//1 2
    System.out.println();
    System.out.println(array[0][0]);//1
    System.out.println(array[0][1]);//2
    System.out.println(array.length);//4
    System.out.println(array[0].length);//2
    for (int i = 0; i <array.length; i++) {
        for (int j = 0; j <array[i].length ; j++) {
            System.out.print(array[i][j]+" ");//1 2 2 3 3 4 4 5 
        }
    }
}
//打印数组元素
public static void printArray(int[] arrays){
    for (int i = 0; i <arrays.length; i++) {
        System.out.print(arrays[i]+" ");
    }
}

Arrays类

public static void main(String[] args) {
    int[]a={1,2,3,4,9090,32,21,6,23};
    System.out.println(a);
    //打印数组元素
    System.out.println(Arrays.toString(a));
    printArray(a);
    Arrays.sort(a);//数组进行排序:升序
    System.out.println();
    System.out.println(Arrays.toString(a));//[1, 2, 3, 4, 6, 21, 23, 32, 9090]
    Arrays.fill(a,2,4,0);
    System.out.println(Arrays.toString(a));//[1, 2, 0, 0, 6, 21, 23, 32, 9090]
}
public static void printArray(int[] a){
    for (int i = 0; i <a.length; i++) {
        if(i==0){
            System.out.print("[");
        }
        if(i==a.length-1){
            System.out.print(a[i]+"]");
        }else{
            System.out.print(a[i]+", ");
        }
    }
}

冒泡排序

  1. 时间复杂度O{n^2}

public static void main(String[] args) {
        int[]a={1,3,5,23,2,7,6};
        int[]sort=sort(a);
        System.out.println(Arrays.toString(sort));
    }
    //冒泡排序
    //1.比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就交换他们的位置
    //2.每一次比较,都会产生出一个最大或者最小的数字
    //3.下一轮则可以少一次排序
    //4.依次循环,知道结束
    public static int[] sort(int[]array){
        //外层循环,判断我们这个冒泡排序要走多少次成序
        for (int i = 0; i <array.length-1 ; i++) {
            boolean flag=false;//通过flag标识位减少没有意义的比较。
            //内层循环,每次找到最大的,比较的次数少i次
            for (int j = 0; j <array.length-1-i ; j++) {
                //比较两个数,如果第一个数比第二个数大,则交换位置。
                if(array[j]>array[j+1]){
                    int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                    flag=true;
                }
            }
            if(flag==false){
                break;
            }
        }
        return array;
    }

稀疏数组

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

  2. public static void main(String[] args) {
        //1.创建一个二维数组  11*11  0:没有棋子 1:黑棋    2:白棋
        int[][] array1=new int[11][11];
        array1[1][2]=1;
        array1[2][3]=2;
        //输出原始的数组
        System.out.println("输出原始的数组");
        for(int[]ints:array1){
            for (int anInt:ints){
                System.out.print(anInt+"\t");
            }
            System.out.println();
        }
        System.out.println("====================");
        //转换成稀疏数组保存
        //获取有效值的个数
        int sum=0;
        for (int i = 0; i <11 ; i++) {
            for (int j = 0; j <11 ; j++) {
                if(array1[i][j]!=0){
                    sum++;
                }
            }
        }
        System.out.println("有效值的个数为:"+sum);
    
        //2.创建一个稀疏数组的数组
        int[][] array2=new int[sum+1][3];
        array2[0][0]=11;
        array2[0][1]=11;
        array2[0][2]=sum;
        //遍历二维数组,将非零的值,存放到稀疏数组的数组中
        int count=0;
        for (int i = 0; i <array1.length; i++) {
            for (int j = 0; j <array1[i].length ; j++) {
                if(array1[i][j]!=0){
                    count++;
                    array2[count][0]=i;
                    array2[count][1]=j;
                    array2[count][2]=array1[i][j];
                }
            }
        }
        //输出稀疏数组
        System.out.println("输出稀疏数组");
        for (int i = 0; i <array2.length; i++) {
            for (int j = 0; j <array2[i].length; j++) {
                System.out.print(array2[i][j]+"\t");
            }
            System.out.println();
        }
        System.out.println("===============================");
        System.out.println("还原");
        //1.读取稀疏数组
        int[][] array3=new int[array2[0][0]][array2[0][1]];
        //2.给稀疏数组的元素还原
        for (int i = 1; i <array2.length; i++) {
            array3[array2[i][0]][array2[i][1]]=array2[i][2];
        }
        //3.打印
        System.out.println("输出还原的数组");
        for(int[] ints:array3){
            for(int anInt:ints){
                System.out.print(anInt+"\t");
            }
            System.out.println();
        }
    }

面向对象(oop)

  1. 面向对象的本质就是:以类的方式组织代码,以对象的组织封装数据。

  2. break:跳出switch,结束循环

    return:方法结束

  3. //值传递
    public static void main(String[] args) {
        int a=1;
        System.out.println(a);//1
        Demo04.change(a);
        System.out.println(a);//1
    }
    //返回值为空
    public static void change(int a){
        a=10;
    }
  4. //引用传递:对象,本质还是值传递
    public class Demo05 {
        public static void main(String[] args) {
            Person person=new Person();
            System.out.println(person.name);//null
            Demo05.change(person);
            System.out.println(person.name);//高翔
        }
        public static void change(Person person){
            //person是一个对象:指向的---->Person person=new Person();这是一个具体的人,可以改变属性!
            person.name="高翔";
        }
    }
    //定义了一个Person类,有一个属性:name
    class Person{
        String name;
    
    }

构造器(构造方法)

  1. 和类名相同的方法,没有返回值。

  2. 一个类即使什么都不写,它也会存在一个构造方法(构造器),构造器用来初始化值。

  3. 使用new关键字,本质是在调用构造器。

  4. 有参构造:一旦定义了有参构造,无参就必须显示定义。

  5. 默认初始化:
    数字:0    0.0
    char:u0000
    boolean:false
    引用:null
  6. public class Person {
        //一个类即使什么都不写,它也会存在一个构造方法(构造器)
        //显示的定义构造器
        String name;
        //实例化
        //1.使用new关键字,本质是在调用构造器
        //2.用来初始化值
        public Person(){
    
        }
        //有参构造:一旦定义了有参构造,无参就必须显示定义
        public Person(String name){
            this.name=name;
        }
    
        //alt+insert 生成构造器
    
    }

封装

1.提高程序的安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.系统可维护性增加了
5.属性私有,使用get和set方法

继承

  1. 一个子类只能继承一个父类,子类可以继承父类的public的方法,private修饰父类的属性,子类通过get,set方法操作。

  2. 在java中,所有的类,都默认直接或者间接继承Object。

  3. 子类的无参构造方法会默认调用父类的无参构造方法,调用父类的构造器,必须在子类的构造器的第一行。

super

super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个
2.super必须只能出现在子类的方法或者构造方法中
3.super和this不能同时调用构造方法

和this的比较:
代表的对象不同:
    this:本身调用这个对象
    super:代表父类对象的应用
    前提:
        this:没有继承也可以使用
        super:只能在继承条件下才能使用
    构造方法
         this();本类的构造
         super();父类的构造

重写

重写:需要有继承关系,子类重写父类的方法!
    1.方法名必须相同
    2.参数列表必须相同
    3.修饰符:范围可以扩大   private<default<protected<public
    4.抛出的异常:范围,可以被缩小,但不能扩大; ClassNotFoundException-->Exception(大)
    
重写,子类的方法和父类必须要一致,方法体不同!
为什么需要重写:
    1.父类的功能,子类不一定需要,或者不一定满足!
    2.Alt+Insert;override; 

多态

  1. 即同一个方法根据发送对象的不同而采用多种不同的行为方式。

  2. 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多,父类的引用指向子类的对象,Person person=new Student();

  3. 对象能执行哪些方法,主要看对象左边的类型,和右边关系不大。

  4. 多态注意事项:
    1.多态是方法的多态,属性没有多态
    2.父类和子类,有联系,否则类型转换异常(ClassCastException)
    3.存在条件:继承关系,方法需要重写,父类引用指向子类对象。 Father f1=new Son();
           1.static方法属于类,不属于实例
           2.final修饰的是常量,方法无法重写,参数无法改变,存在常量池
           3.private方法不能重写

instanceof和引用类型转换

对象运算符(instanceof)用来判断一个对象是否属于某个指定的类或其子类的实例,如果是,返回真(true),否则返回假(false)。

  1. //Object>String
    //Objenct>Person>Teacher
    //Object>Person>Student
            Object object=new Student();
            System.out.println(object instanceof Student);//true
            System.out.println(object instanceof Person);//true
            System.out.println(object instanceof Object);//true
            System.out.println(object instanceof Teacher);//false
            System.out.println(object instanceof String);//false
            System.out.println("==================================");
            Person person=new Student();
            System.out.println(person instanceof Student);//true
            System.out.println(person instanceof Person);//true
            System.out.println(person instanceof Object);//true
            System.out.println(person instanceof Teacher);//false
            //System.out.println(person instanceof String);//编译报错
            System.out.println("==================================");
            Student student=new Student();
            System.out.println(student instanceof Student);//true
            System.out.println(student instanceof Person);//true
            System.out.println(student instanceof Object);//true
            //System.out.println(student instanceof Teacher);//编译报错
            //.out.println(student instanceof String);//编译报错
  2. public static void main(String[] args) {
        //引用类型之间的转化:父  子
        //高到低,父类转子类
        Person obj=new Student();
        //obj将这个对象转换为student类型,我们就可以使用studnet类型的方法了
        Student student=(Student)obj;
        student.go();
    
    
        //低到高,子类转父类,可能丢失自己的本来一些方法
        Student student1=new Student();
        student1.go();
        Person person=student;
        ((Student) person).go();
    }
  3. /*
    1.父类引用指向子类对象
    2.把子类转换成父类,向上转型,可能会丢失自己的本来一些方法。
    3.把父类转换成子类,向下转型,强制转换
    4.方便方法的调用,减少重复代码!简介
     */

static

  1. //static
    public class Student {
        private static int age;//静态的变量
        private double score;//非静态的变量
    
        public void run(){
            go();
        }
        public static void go(){
            //run();错误
        }
        public static void main(String[] args) {
            Student s1 = new Student();
           // System.out.println(Student.score);错误
            System.out.println(Student.age);//0
            System.out.println(s1.age);//0
            System.out.println(s1.score);//0.0
            go();
        }
    }
  2. //代码块
    public class Person {
        //2:赋初值
        {
            //代码块(匿名代码块)
            System.out.println("匿名代码块");
        }
        //1:只执行一次
        static {
            //静态代码块
            System.out.println("静态代码块");
        }
        //3
        public Person(){
            System.out.println("构造方法");
        }
    
        public static void main(String[] args) {
            Person person = new Person();
            System.out.println("===============");
            Person person1 = new Person();
            /*
            静态代码块
            匿名代码块
            构造方法
            ===============
            匿名代码块
            构造方法
             */
        }
    }

抽象类

  1. abstract修饰的类

  2. 抽象方法必须在抽象类中,抽象方法是abstract修饰的方法,只有方法名字,没有方法实现

  3. 抽象类的所有方法,继承了它的子类,都必须要实现它的方法

  4. 抽象类中可以写普通方法

  5. 不能new这个抽象类,只能靠子类去实现它:约束!

接口

  1. interface声明的类,接口需要有实现类。

  2. 接口中的所有定义都是抽象的public,public abstract void run(); 默认简化void run();

  3. 接口不能实例化,接口中没有构造方法。

  4. implements可以实现多个接口。

  5. 必须要重写接口中的方法。

内部类

  1. 就是在一个类的内部在定义一个类。

  2. 内部类可以直接方法外部类方法和属性,不需要创建外部类的对象。

  3. 一个java文件可以有多个class类,但是只能有一个public class

成员内部类

public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    public class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }
        //获得外部类的私有属性,私有方法
        public void getID(){
            out();
            System.out.println(id);
        }
    }
}

静态内部类

public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }
    //静态内部类无法直接访问外部类的属性方法了
    public static class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }
    }
}

局部内部类

public class Outer {
    //局部内部类
    public  void method(){
        class Inner{
            public void in(){
                System.out.println("局部内部类的方法");
            }
        }
    }
}

匿名内部类

public class Test {
    public static void main(String[] args) {
        //没有名字初始化类,不用将实例保存到变量中
        new Apple().eat();
        new UserService(){
            @Override
            public void hello() {
                
            }
        };
    }
}
class Apple{
    public void eat(){
        System.out.println("1");
    }
}

interface UserService{
    void hello();
}

异常

  1. Throwable:

    • Exception:运行时异常:1. 1/0 2.ClassNotFound 3.NullPoint 4.UnkownType 5.ArrayIndexOutofBounds

      检查型异常

    • Error:(前端布局错误)AWT错误

      JVM错误 : 1.StackOverFlow 2.OutofMemory

  2. Error和Exception的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常时,java虚拟机(JVM)一般会终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

  3. public class Test {
        public static void main(String[] args) {
            int a=1;
            int b=0;
            //假设要捕获多个异常:要从小到大
            try{//try监控区域
                System.out.println(a/b);
            }catch (Error e){//catch(想要捕获的异常类型)
                System.out.println("Error");
            }catch(Exception e){
                System.out.println("Exception");
            }catch (Throwable t){
                System.out.println("Throwable");
            } finally {//处理善后工作,finally可以不要,假设IO,资源关闭。
                System.out.println("finally");
            }
        }
    }
  4. public static void main(String[] args) {
        try {
            new Test2().test(1,0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
    
    }
    //假设这方法中,处理不了这个异常。方法上抛出异常
    public void test(int a,int b)throws ArithmeticException{
        if (b==0){// throw  throws
            throw new ArithmeticException();//主动抛出异常,一般在方法里使用
        }
        System.out.println(a/b);
    }

自定义异常

//自定义异常
public class MyException extends Exception {
    //传递数字>10;
    private  int detail;

    public MyException(int detail) {
        this.detail = detail;
    }


    //toString:异常的打印信息
    @Override
    public String toString() {
        return "MyException{" +
                "detail=" + detail +
                '}';
    }
}




public class Test {
    //可能会存在异常的方法
    static void test(int a) throws MyException {
        System.out.println("传递的参数为:"+a);
        if(a>10){
            throw new MyException(a);//抛出
        }
        System.out.println("OK");
    }
    public static void main(String[] args){
        try {
            test(11);
        } catch (MyException e) {
            System.out.println("MyException=>"+e);
        }
    }
}

Java API(Application Programming Interface)

  1. 中文名:应用程序编程接口,通常叫“Java文档”,是Java中提供的类的使用说明书,是程序员和Java语言之间沟通的桥梁。

  2. 学习API其实就是学习如何使用JDK提供的类。

  3. Java API可以在Oracle官网下载这是jdk8的,也可以找些中文Java API。

Object(java.lang)

  1. 类层次结构最顶层的基类,所有类都直接或间接的继承自Object类,所以,所有的类都是一个Object(对象)。

  2. 所有类由Object():构造一个对象,子类对象初始化时都会优先调用该方法 。

  3. int hashCode();返回对象的哈希码值,该方法通过对象的地址值进行计算,不同对象的返回值一般不同。

  4. class<?>getClass();返回调用此方法对象的运行时类对象(调用者的字节码文件对象)。

  5. String toString();返回该对象的字符串表示。

  6. boolean equals();返回其它某个对象是否与此对象“相等”。默认情况下比较两个对象的引用(地址值),建议重写 。

  7. //重写toString()和equals()
    public class Student {
        private int id;
        private String name;
        private double score;
    
        public Student(int id, String name, double score) {
            this.id = id;
            this.name = name;
            this.score = score;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public double getScore() {
            return score;
        }
    
        public void setScore(double score) {
            this.score = score;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", score=" + score +
                    '}';
        }
    
        @Override
        public boolean equals(Object obj) {
            //s1.equals(s2);
            //this: s1   o: s2
            //比较两个对象的地址值是否相同, 提高效率.
            //补充: 如果if语句控制的语句体只有一行代码,那么{}可以省略不写.
            if (this == obj)
                return true;
            //判断要比较的两个对象是否是同一个类型的对象, 提高程序的健壮性.
            if (obj == null || getClass() != obj.getClass())
                return false;
            //向下转型, 正常的逻辑代码.
            Student student = (Student) obj;
            return id == student.id &&
                    score == student.score &&
                    name.equals(student.name);
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            //测试toString()方法.
            //创建学生类的对象.
            Student s1 = new Student(1, "欧阳修", 66);
            //输出语句直接打印对象, 默认调用了该对象的toString()方法.
            System.out.println(s1);
            System.out.println(s1.toString());
    
            //测试equals()方法,  s1.equals(s2);
            Student s2 = new Student(1, "欧阳修", 66);
            boolean b1 = s1.equals(s2);
            System.out.println(b1);
        }
    }

Scanner类(java.util)

  1. Scanner(InputSteam);构造一个扫描器对象,从指定输入流中获取数据参数System.in,对应键盘录入。

  2. hasNextXxx();判断是否还有下一个输入项,其中Xxx可能是任意基本数据类型,返回结果为布尔类型。

  3. nextXxx();获取下一个输入项,其中Xxx可能是任意基本数据类型,返回对应类型的数据。

  4. String nextLine();获取下一行数据。以换行符作为分隔符。

  5. String next();获取下一个输入项,以空白字符作为分隔符 空白字符:空格、tab、回车等。

  6. public class Test {
        public static void main(String[] args) {
            //创建Scanner类型的对象(注意: 要导包)
            //System.in: 标准的输入流, 默认指向键盘.
            Scanner sc = new Scanner(System.in);
    
            //接收整数.
            /*System.out.println("请录入一个整数: ");
            //为了解决(避免)InputMismatchException异常, 可以加入一个判断
            if (sc.hasNextInt()) { //判断下一个录入的是否是整数, 如果是, 结果就是true
                int num = sc.nextInt();
                System.out.println("num: " + num);
            }*/
    
            //接收字符串类型的数据
            System.out.println("请录入一个字符串:");
            /*String str1 = sc.nextLine();   //结束标记是: 换行符
            System.out.println("str1: " + str1);*/
    
            String str2 = sc.next();        //结束标记: 空白字符(空格, tab, 换行符)
            System.out.println("str2: " + str2);
            sc.close();
        }
    }

String类(String)

  1. int length();获取当前字符串的长度。

  2. char charAt(int index);获取指定索引位置的字符。

  3. int indexOf(String);获取指定字符(串)第一次出现的索引。

  4. int lastIndexOf(String);获取指定字符(串)最后一次次出现的索引。

  5. String substring(int);获取指定索引位置(含)之后的字符串。

  6. String substring(int,int);获取从索引start位置(含)起至索引end位置(不含)的字符串。

  7. public class Test {
        public static void main(String[] args) {
            //定义一个字符串
            String str = "java 回车课堂 java";
    
            //int length():               获取当前字符串的长度
            /*int length = str.length();
            System.out.println(length);*/
            System.out.println(str.length());
    
            //char charAt(int index):     获取指定索引位置的字符
            char ch = str.charAt(1);
            System.out.println(ch);
    
            //int indexOf(String):        获取指定字符(串)第一次出现的索引
            //需求: 字符'a'第一次出现的位置
            int index1 = str.indexOf('a');
            System.out.println("index1: " + index1);
    
            //int lastIndexOf(String):    获取指定字符(串)最后一次次出现的索引
            //需求: 字符'a'最后一次出现的位置
            int index2 = str.lastIndexOf('a');
            System.out.println("index2: " + index2);
    
            //String substring(int):      获取指定索引位置(含)之后的字符串
            //需求: 截取从索引5开始的所有元素
            String s1 = str.substring(5);
            System.out.println("s1: " + s1);
    
            //String substring(int, int): 获取从索引start位置(含)起至索引end位置(不含)的字符串
            //需求: 截取索引5, 10的所有元素
            String s2 = str.substring(5,10);
            System.out.println("s2: " + s2);  //?
        }
    }
  8. byte[] getBytes();将字符串转换成字节数组

  9. char[] toCharArray(); 将字符串转换成字符数组。

  10. static String valueOf();将指定类型数据转换成字符串。

  11. String replace(old,new);将指定字符(串)替换成新的字符(串)。

  12. String[] split(String);切割字符串,返回切割后的字符串数据,原字符串不变。

  13. String trim();去掉字符串两端的空白字符。

  14. public class Test01 {
        public static void main(String[] args) {
            //定义一个字符串
            String s1 = "abc";
    
            //byte[] getBytes():          将字符串转换成字节数组
            byte[] bys = s1.getBytes();    //97, 98, 99
            for (int i = 0; i < bys.length; i++) {
                System.out.println(bys[i]);
            }
            System.out.println("---------------------");
    
            //char[] toCharArray():       将字符串转换成字符数组
            char[] chs = s1.toCharArray(); //'a','b','c'
            for (int i = 0; i < chs.length; i++) {
                System.out.println(chs[i]);
            }
            System.out.println("---------------------");
    
            //static String valueOf(..) : 将指定类型数据转换成字符串
            //整数123  -->  字符串"123"
            String s2 = String.valueOf(123);
            System.out.println(s2 + 4);
            //在实际开发中, 上述的方式基本上都会用下边的这行代码替代
            String s3 = "" + 123;
            System.out.println(s3 + 4);
            System.out.println("---------------------");
    
            //String replace(old, new):   将指定字符(串)替换成新的字符(串)
            String s4 = "abc abc abc";
            //'d'  替换 'b'
            String s5 = s4.replace('b','d');
            System.out.println("s5: " + s5);
            System.out.println("---------------------");
    
            //String[] split(String):     切割字符串,返回切割后的字符串数据,原字符串不变
            //将字符串s4, 按照空格进行切割
            //"abc" "abc" "abc"
            String[] arr = s4.split(" ");
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
            System.out.println("---------------------");
    
            //String trim():              去掉字符串两端的空白字符
            String s6 = "  a   b   c   ";
            String s7 = s6.trim();
            System.out.println("s6: " + s6);
            System.out.println("s7:" + s7);
        }
    }

StringBuilder和StringBuffer(java.lang)

  1. StringBulider();构造一个空的StringBuilder容器。

  2. StringBulider(String);构造一个StringBuilder容器,并添加指定字符串。

  3. StringBulider append(...);将任意数据添加到StringBuilder容器中。

  4. String toString();将当前StringBuilder容器转成字符串。

  5. public class Test {
        public static void main(String[] args) {
            //测试构造方法
            //测试空参构造
            StringBuilder sb = new StringBuilder();
            StringBuilder sb2 = sb.append("abc");
            System.out.println("sb: " + sb);
            System.out.println("sb2: " + sb2);
            System.out.println("----------------");
            //需求: 将String类型的"abc" 转成 StringBuilder类型的对象
            StringBuilder sb3 = new StringBuilder("abc");
            System.out.println("sb3: "+ sb3);
            System.out.println("----------------");
    
            //测试成员方法
            //需求: 将三个字符串拼接成一个新的字符串:  学Java, 到传智播客 找小黑!
            StringBuilder sb4 = new StringBuilder();
            sb4.append("学Java,");
            sb4.append("到传智播客");
            sb4.append("找小黑!");
            System.out.println("sb4: " + sb4);
            System.out.println("----------------");
            String s = sb4.toString();
            System.out.println("字符串s: " + s);
        }
    }

6.String和StringBulider以及StringBuffer的区别:

  • String不可变字符,StringBuilder可变字符、效率高、线程不安全,StringBuffer可变字符、效率低、线程安全。

Data和Calendar(java.util)

  1. Date();构造一个日期对象,当前系统时间,精确到毫秒。

  2. Date(long)l;构造一个日期对象,时间为自“1970年1月1日00:00:00 GMT”起,至指定参数的毫秒数。

  3. long getTime();**将日期对象转换成对应时间的毫秒值 。

  4. public class Test {
        public static void main(String[] args) {
            //测试Date类
            //测试空参构造, 采用当前操作系统的默认时间
            Date date1 = new Date();
            System.out.println("date1:" + date1);
            //获取当前操作系统时间的毫秒值
            long time = date1.getTime();
            System.out.println("time:" + time);
            //Sun Jun 06 17:04:39 CST 2066   --> 3043040679456
            //创建一个指定的时间
            Date date2 = new Date(3043040679456L);
            System.out.println("date2:" + date2);
        }
    }
  5. static Calendar getInstance();根据当前系统时区和语言环境获取日历对象。

  6. int get(int field);返回给定日历字段的值。

  7. void set(int field,int value);将给定的日历字段设置为指定的值。

  8. public class Test01 {
        public static void main(String[] args) {
            //创建Calendar类型的对象.
            Calendar c = Calendar.getInstance();
            System.out.println(c);
            //获取年月日的信息
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);  //Java中使用0-11的数字表示月份的, 对应1-12月
            int day = c.get(Calendar.DATE);
            System.out.println(year + "年" + (month + 1) + "月" + day + "日");
            System.out.println("------------------------");
            //设置指定时间为: 2022年2月2日
           /* c.set(Calendar.YEAR,2022);
            int year2 = c.get(Calendar.YEAR);
            System.out.println(year2 + "年" + (month + 1) + "月" + day + "日");*/
            c.set(2022, 1, 2);
            //重新获取年月日的值
            int year2 = c.get(Calendar.YEAR);
            int month2 = c.get(Calendar.MONTH);  //Java中使用0-11的数字表示月份的, 对应1-12月
            int day2 = c.get(Calendar.DATE);
            System.out.println(year2 + "年" + (month2 + 1) + "月" + day2 + "日");
        }
    }

基本类型的包装类

  1. 由于基本数据类型不是对象,所以不能直接调用方法,故Java针对基本类型提供了对应的包装类,以对象的形式来使用。

  2. byte=>Byte;short=>Short;int=>Integet;long=>Long;char=>Character;float=>Float;double=>Double;boolean=>Boolean;

  3. 装箱:基本类型转包装类型(对象类型)。

  4. 拆箱:包装类型(对象类型转基本类型)。

  5. 基本类型parseXxx(String);将字符串类型的数据转换成对应的基本类型。例如:static int parseInt(String);

  6. 除了Character类以外, 其他的7种包装类都有parseXXX()方法. 因为如果字符串想转换成char类型的数据, 可以通过: String类中的方法toCharArray(), charAt();

  7. public class Test {
        public static void main(String[] args) {
            //因为变量a属于基本类型, 不能通过 对象名. 的形式调用方法.
            //解决方案: 将其转换成对应的 包装类(引用类型)即可.
            int a = 10;
    
            //装箱
            Integer i1 = new Integer(20);
            //拆箱
            int b = i1.intValue();
            System.out.println(i1);
            System.out.println(b);
            System.out.println("------------------");
    
            //JDK5的新特性, 自动拆装箱.
            Integer i2 = 30;        //装箱
            int c = i2;             //拆箱
            System.out.println("---------------------------");
    
            //需求: 将字符串类型的"10", 转换成int类型的 10
            String s = "10";
            int num = Integer.parseInt(s);
            System.out.println("num: " + num);
            System.out.println("num + 100 = " + (num + 100));
    
        }
    }

IO流

  1. I/O,即输入(Input)输出(Output),IO流指的是数据像连绵的流体一样进行传输,可以在本地磁盘或网络上传输(读/写)数据。

  2. 字符流:按字符读写数据的IO流

    • Reader: 字符输入流的顶层抽象类

      • FileReader: 普通的字符输入流.

      • BufferedReader: 高效的字符输入流(也叫: 字符缓冲输入流)

    • Writer:字符输出流的顶层抽象类

      • FileWriter: 普通的字符输出流

      • BufferedWriter: 高效的字符输出流(也叫: 字符缓冲输出流)

    字节流: 以字节为单位来操作数据

    • InputStream: 字节输入流的顶层抽象类

      • FileInputStream: 普通的字节输入流

      • BufferedInputStream: 高效的字节输入流(也叫: 字节缓冲输入流)

    • OutputStream: 字节输出流的顶层抽象类

      • FileOutputStream: 普通的字节输出流.

      • BufferedOutputStream: 高效的字节输出流(也叫: 字节缓冲输出流)

File类

  1. 文件,文件夹,一个File对象代表磁盘上的某个文件或文件夹。

  2. File(String pathname) File(String parent, String child) File(File parent, String child)

  3. boolean createNemFile();创建文件。

  4. boolean mkdir();和boolean mkdirs();创建目录。

  5. isDirectory();判断File对象是否为目录。

  6. isFile();判断File对象是否为文件。

  7. exists();判断File对象是否存在。

  8. getAbsoulutePath();获取绝对路径。绝对路径指的是从本地磁盘开始的路径。

  9. getPath();获取文件的相对路径。相对路径指的是相对某一位置的路径,在Java项目中,相对路径从项目名开始。

  10. getName();获取文件名。

  11. list();获取指定目录下所有文件(夹)名称数组。

  12. listFiles();获取指定目录下所有文件(夹)File数组。

  13. 将 C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt 封装成File对象
    public class Test {
        public static void main(String[] args) throws IOException {
            //方式一: 根据字符串形式的路径获取File对象.
            File file1 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
            System.out.println("file1: " + file1);
    
            //方式二: 根据字符串形式的父目录以及子目录创建File对象.
            File file2 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\", "1.txt");
            System.out.println("file2: " + file2);
    
            //方式三: 根据父目录对象, 以及字符串形式的子目录来获取File对象.
            File file3 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\");
            File file4 = new File(file3, "1.txt");
            System.out.println("file4: " + file4);
        }
    }
  14. //在C:\\Users\\GX\\Desktop\\杂货\\File类学习\\下创建文件和文件夹
    public class Test01 {
        public static void main(String[] args) throws IOException {
            File file5 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt");
            boolean flag1 = file5.createNewFile();
            System.out.println("flag1: " + flag1);
    
            //需求: 在D:盘下创建 a文件夹
            File file6 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\a");
            boolean flag2 = file6.mkdir();   //make directory, 创建单级目录
            System.out.println("flag2: " + flag2);
    
            //需求: 在D:盘下创建 a/b/c文件夹
            File file7 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\a\\b\\c");
            boolean flag3 = file7.mkdirs();     //创建多级目录(也可以创建单级目录)
            System.out.println("flag3: " + flag3);
        }
    }
  15. //运用成员方法对文件/文件夹做判断
    public class Test02 {
        public static void main(String[] args) throws IOException {
            File file8 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
           /* boolean flag4 = file8.isDirectory();
            System.out.println("测试file8是否是文件夹: " + flag4 );*/
            System.out.println("测试file8是否是文件夹: " + file8.isDirectory() );
            System.out.println("测试file8是否是文件: " + file8.isFile());
            System.out.println("测试file8是否存在: "  + file8.exists());
        }
    }
  16. //获取绝对路径
    //获取文件的相对路径
    //获取文件名
    //获取指定目录下所有文件(夹)名称数组
    //获取指定目录下所有文件(夹)File数组
    public class Test03 {
        public static void main(String[] args) {
            File file1 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
            //获取file1的绝对路径
            String path1 = file1.getAbsolutePath();
            System.out.println("绝对路径: " + path1);
    
            //获取file1的相对路径
            String path2 = file1.getPath();
            System.out.println("相对路径: " + path2);
    
            //获取文件名
            String fileName = file1.getName();
            System.out.println("文件名: " + fileName);
            System.out.println("--------------------");
    
            //获取lib文件夹下所有的文件(夹)的: 名称数组String[]
            File file2 = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习");
            String[] names = file2.list();
            for (String name : names) {
                System.out.println(name);
            }
            System.out.println("--------------------");
    
            //获取lib文件夹下所有的文件(夹)的: File对象数组 File[]
            File[] files = file2.listFiles();
            for (File file : files) {
                System.out.println(file);
            }
        }
    }

字符流读写文件

//1.txt文件中存储着字符串“abc”,请通过字符流读取数据
public class ReaderDemo1 {
    public static void main(String[] args) throws IOException {
        //需求: 通过字符流读取数据.
        //1. 创建字符输入流对象.
        Reader reader = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
        //2. 读取数据.
        //定义变量, 用来接收读取到的字符.
        int ch;
        while((ch = reader.read()) != -1){
            //ch = reader.read();
            System.out.println(ch);
        }

        //3. 释放资源.
        reader.close();
    }
}
//2.txt文件中存储着字符串“abcdef”,要求通过字符流读取数据, 一次读取一个字符数组
public class ReaderDemo2 {
    public static void main(String[] args) throws IOException {
        //需求: 通过字符流读取数据, 一次读取一个字符数组.
        //1. 创建字符输入流对象.
        Reader reader = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt");
        //2. 读取数据.
        //定义字符数组
        char[] chs = new char[3];
        //定义一个变量, 记录读取到的有效字符数.
        int len;
        while((len = reader.read(chs)) != -1) {
            //将读取到的内容, 转换成字符串, 然后打印.
            String s = new String(chs,0,len);    //[g,e,f], 0,  1  chs: 表示要操作的数组 0: 表示起始索引 len: 表示要操作的字符的个数
            System.out.println(s);
        }

        //3. 释放资源.
        reader.close();
    }
}
//字符流写数据 – 按单个字符写入
public class WriteDemo {
    public static void main(String[] args) throws IOException {
        //1. 创建字符输出流对象.
        Writer writer = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
        //2. 写数据.
        //一次写一个字符
        writer.write('好');
        //3. 释放资源.
        writer.close();
    }
}
//字符流写数据 – 按字符数组/字符串写入
public class WriteDemo01 {
    public static void main(String[] args) throws IOException {
        //1. 创建字符输出流对象.
        Writer writer = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
        //2. 写数据.

        //一次写一个指定的字符数组
        char[] chs = {'黑','马','程','序','员'};
        writer.write(chs,2,3);

        //一次写一个字符串
        writer.write("好好学习, 知识改变命运!");

        //3. 释放资源.
        writer.close();
    }
}
//通过字符流拷贝文件, 一次读写一个字符(将1.txt文件中的内容复制到2.txt文件中)
public class CopyFile {
    public static void main(String[] args) throws IOException {
        //1 创建字符输入流对象, 关联数据源文件
        //Reader reader = new FileReader("lib/1.txt");
        FileReader fr = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
        //2 创建字符输出流对象, 关联目的地文
        FileWriter fw = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
        //3 定义变量,记录读取到的内容
        int len;
        //4 循环读取,只要条件满足就一直读,并将读取到的内容赋值给变
        while ((len = fr.read()) != -1) {
            //5 将读取到的数据写入到 目的地文件中
            fw.write(len);
        }
        //6 释放资源
        fr.close();
        fw.close();
    }
}
//通过字符缓冲流, 将1.txt文件中的内容拷贝到2.txt文件中
public class CopyFile02 {
    public static void main(String[] args) throws IOException {
        //1. 创建字符缓冲输入流对象, 关联数据源文件.
        //1.1 创建普通的字符输入流对象
        FileReader fr = new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt");
        //1.2 创建字符缓冲输入流对象
        BufferedReader br = new BufferedReader(fr);
        //简化上述的代码
        //BufferedReader br2 = new BufferedReader(new FileReader("lib/1.txt"));

        //2. 创建字符缓冲输出流对象, 关联目的地文件.
        //2.1 创建普通的字符输出流对象.
        FileWriter fw = new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt");
        //2.2 创建字符缓冲输出流对象.
        BufferedWriter bw = new BufferedWriter(fw);

        //3. 定义变量, 记录读取到的数据.
        int len;
        //4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while((len = br.read()) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bw.write(len);
        }
        //6. 释放资源.
        br.close();
        bw.close();
    }
}
//通过字符缓冲流"一次读写一行"的方式, 将1.txt文件中的内容拷贝到2.txt文件中
public class CopyFile03 {
    public static void main(String[] args) throws IOException {
        //1. 创建字符缓冲输入流对象, 关联数据源文件.
        BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.txt"));

        //2. 创建字符缓冲输出流对象, 关联目的地文件.
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.txt"));

        //3. 定义变量, 记录读取到的内容.
        String str;
        //4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while((str = br.readLine()) != null) {
            //5. 将读取到的内容写入到目的地文件中.
            bw.write(str);
            //换行
            bw.newLine();
        }

        //6. 释放资源.
        br.close();
        bw.close();
    }
}

字符流只能拷贝纯文本文件。

字节流读写文件

//通过普通的字节流, 一次读写一个字节的方式将lib/1.jpg赋值到lib/2.jpg中
public class Steam01 {
    public static void main(String[] args) throws IOException {
        //1.创建字节输入流, 关联数据源文件
        FileInputStream fis = new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg");


        //2.创建字节输出流, 关联目的地文件
        FileOutputStream fos = new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg");
        //3.创建字节输出流, 关联目的地文件
        int len;
        //4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
        while((len = fis.read()) != -1) {
            //5.将读取到的数据写入到目的地文件中
            fos.write(len);
        }
        //6.释放资源
        fis.close();
        fos.close();
    }
}
//通过普通字节流一次读写一个数组的方式将1.jpg复制到2.jpg中
public class Stream02 {
    public static void main(String[] args) throws IOException {
        //1.创建字节输入流对象, 关联数据源文件
        FileInputStream fis = new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg");
        //2.创建字节输出流对象, 关联目的地文件
        FileOutputStream fos = new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg");
        //3.创建字节输出流, 关联目的地文件
        byte[] bys = new byte[1024];
        //用来记录读取到的有效字节数
        int len;
        int sum=0;
        //4.循环读取,只要条件满足就一直读,并将读取到的内容赋值给变量
        while((len = fis.read(bys)) != -1) {
            //5.将读取到的数据写入到目的地文件中
            fos.write(bys,0,len);
        }
        //6.释放资料
        fis.close();
        fos.close();
    }
}
//通过字节缓冲流,将1.jpg文件中的内容拷贝到2.jpg文件中
public class Stream03 {
    public static void main(String[] args) throws IOException {
        //1. 创建字节缓冲输入流对象, 关联数据源文件.
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\1.jpg"));

        //2. 创建字符缓冲输出流对象, 关联目的地文件.
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\2.jpg"));

        //3. 定义变量, 记录读取到的数据.
        int len;
        //4. 循环读取, 只要条件满足就一直读, 并将读取到的内容赋值给变量.
        while ((len = bis.read()) != -1) {
            //5. 将读取到的数据写入到目的地文件中.
            bos.write(len);
        }
        //6. 释放资源.
        bis.close();
        bos.close();
    }
}

使用控制台模拟实际开发中上传用户头像的功能

//使用控制台模拟实际开发中上传用户头像的功能
//分析:
//
//        在控制台录入用户头像的路径
//        解析路径字符串中文件名是否合法:后缀名为:.jpg、.png、.bmp
//        判断该路径表示的File对象是否存在,是否为文件:
//        file.exist()
//        file.isFile()
//        读取该文件并写入到指定目录
//        提示头像上传成功 或 失败
//        实现步骤:
//
//        定义一个方法,用来获取要上传的用户头像路径. getPath(),返回文件对象
//        定义一个方法,用来判断要上传的用户头像,在lib文件夹中是否存在.isExists(String path),需要传入文件名
//        如果存在,提示:该用户头像已经存在,上传失败
//        如果不存在,就上传该用户头像,并提示上传成功,这里也需要定义一个头像上传函数
public class Total {
    public static void main(String[] args)  throws IOException {
        //需求:模拟用户上传头像功能,假设所有的用户头像都应该上传到:项目下的目标文件夹中
        //1. 定义一个方法,用来获取要上传的用户头像路径. getPath();
        File path =getPath();
        System.out.println(path);  
        //2. 定义一个方法,用来判断要上传的用户头像,在文件夹中是否存在
        boolean flag = isExists(path.getName());

        //3. 如果存在,提示:该用户头像已经存在,上传失败
        if (flag) {
            System.out.println("该用户头像已经存在,上传失败");
        } else {
            //4. 如果不存在,就上传该用户头像,并提示上传成功
            //数据源文件                        目的地文件
            //d:\picture\1.png  ——>            lib/1.png
            uploadFile(path);
        }
    }
        public static File getPath() {
        //1.1 提示用户录入要上传的用户头像路径,并接收
        Scanner sc = new Scanner(System.in);
        //1.2 因为不知道用户多少次能录入正确,所以用while(true)改进
        while (true) {
            System.out.println("请录入您要上传的用户头像路径");
            String path = sc.nextLine();
            //1.3 判断该路径的后缀名是否是:.jpg  .png  .bmp
            //1.4 如果不是,就提示您录入的不是图片,请重新录入;
            if (!path.endsWith(".jpg") && !path.endsWith(".png") && !path.endsWith("bmp")) {

                System.out.println("您录入的不是图片,请重新录入:");
                //细节,千万注意,别忘了写
                continue;
            }

            //1.5 如果是,程序接着执行,并判断该路径是否存在,并且是否是文件
            File file = new File(path);
            if (file.exists() && file.isFile()) {
                //1.6 如果是,说明就是我们想要的数据(图片,文件),直接返回
                return file;
            } else {
                //1.7 如果不是,就提示:您录入的路径不合法,请重新录入;
                System.out.println("您录入的路径不合法,请重新录入:");

            }

        }
    }

    public static boolean isExists(String path) {//1.png
        //2.1 将lib文件夹封装成File对象
        File file = new File("C:\\Users\\GX\\Desktop\\杂货\\File类学习");
        //2.2 获取lib文件夹中所有文件(夹)的名称数组
        String[] names = file.list();
        //2.3 遍历第二步获取到的数组,用获取到的数据一次和path进行比较
        for (String name : names) {
            if (name.equals(path)) {
                //2.4 如果一致,说明该用户头像已经存在了,就返回true
                return true;
            }
        }
        //2.5 如果不一致,说明该用户头像不存在,就返回false
        return false;
    }

    public static void uploadFile(File path) throws IOException {
        //4.1 创建字节输入流,关联数据源文件
        //FileInputStream(File file)
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));   //paht: d:\picture\1.png
        //4.2 创建字符输出流,关联目的地文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:\\Users\\GX\\Desktop\\杂货\\File类学习\\" + path.getName())); // lib/1.png
        //4.3 定义变量,记录读取到的数据
        int len;
        //4.4 循环读取,只要条件满足就一直读,并将读取到的数据赋值给变量
        while ((len = bis.read()) != 1) {
            //4.5 将读取到的数据写入到目的地文件夹
            bos.write(len);
        }
        //4.6 释放资源
        bis.close();
        bos.close();
    }
}

多线程

  1. 程序:是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

  2. 进程:是程序的一次执行过程,它是一个动态的概念。是系统资源分配的单位。

  3. 一个进程可以包含多个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是cpu调度和执行的单位。

    第一种方法继承Tread类,重写run();方法,子类对象.start(),不建议使用因为单继承局限性

  4. //创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
    //总结:注意:线程开启不一定立即执行,由cpu调度执行
    public class TestThread extends Thread{
        @Override
        public void run() {
            //run方法线程体
            for (int i = 0; i <20 ; i++) {
                System.out.println("我在看代码----"+i);
            }
        }
    
        public static void main(String[] args) {
            //main线程,主线程
            //创建一个线程对象
            TestThread testThread1=new TestThread();
            //调用start()方法开启线程,同时执行
            testThread1.start();
    
            for (int i = 0; i <20 ; i++) {
                System.out.println("我在学习多线程--"+i);
            }
        }
    }
  5. //练习Thread,是实现多线程同步下载图片
    public class TestThread01 extends Thread{
        private String url;//网络图片地址
        private String name;//保存的文件名
        public TestThread01(String url,String name){
            this.url=url;
            this.name=name;
        }
        @Override
        public void run() {
            WebDownloader webDownloader=new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载的文件名为:"+name);
        }
    
        public static void main(String[] args) {
            TestThread01 testThread01=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果1");
            TestThread01 testThread02=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果2");
            TestThread01 testThread03=new TestThread01("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果3");
            testThread01.start();//线程不是顺序执行的
            testThread02.start();
            testThread03.start();
        }
    }
    
    //下载器
    class WebDownloader{
        //下载方法
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO异常,downloader方法出现问题");
            }
        }
    }

第二种方法定义MyRunnable类实现Runnable接口,重写run();方法,编写线程执行体,创建线程对象,调用start();方法启动线程,推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用。

//创建线程方法2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start();方法
public class TestThread02 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i <20 ; i++) {
            System.out.println("我在看代码----"+i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread02 testThread02=new TestThread02();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        Thread thread=new Thread(testThread02);
        //调用start()方法开启线程,同时执行
        thread.start();

        for (int i = 0; i <20 ; i++) {
            System.out.println("我在学习多线程--"+i);
        }
    }
}
//多个线程同时操作同一个对象
//买火车票的例子
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
public class TestThread03 implements Runnable{
    //票数
    private int ticketNums=10;
    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            try {
                //模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了"+ticketNums--+"票");
        }
    }

    public static void main(String[] args) {
        TestThread03 testThread03=new TestThread03();
        new Thread(testThread03,"小明").start();
        new Thread(testThread03,"老师").start();
        new Thread(testThread03,"黄牛").start();
    }
}
//模拟龟兔赛跑
public class Race implements Runnable {
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <=100; i++) {
            //模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag=gameOver(i);
            //如果比赛结束了,就停止程序
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        }
    }
    //判断是否完成比赛
    private boolean gameOver(int steps){
        //判断是否有胜利者
        if(winner!=null){
            //已经存在胜利者了
            return true;
        }else{
            if(steps>=100){
                winner=Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

实现Callable接口

//线程创建方式三:实现callable接口
/*
callable的好处
1.可以定义返回值
2.可以抛出异常
 */
public class TestCallable implements Callable {
    private String url;//网络图片地址
    private String name;//保存的文件名
    public TestCallable(String url, String name){
        this.url=url;
        this.name=name;
    }
    @Override
    public Boolean call() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果1");
        TestCallable t2=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果2");
        TestCallable t3=new TestCallable("https://ts1.cn.mm.bing.net/th/id/R-C.bcea842bd4040811910ebb18a59bc993?rik=xfMsFJMc9BvZEA&riu=http%3a%2f%2fpic39.photophoto.cn%2f20160601%2f1155115774157522_b.jpg&ehk=B1d6su6%2b%2f6qrwZWpmaNP5i9Jom347235JEV4TviTFgI%3d&risl=&pid=ImgRaw&r=0","苹果3");
        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t2);
        Future<Boolean> r3=ser.submit(t3);
        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        //关闭服务
        ser.shutdownNow();
    }
}
FutureTask<Integer> futureTask=new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();

Lamda表达式

  1. 定义一个函数式接口(只有一个抽象方法的接口)。

  2. 避免匿名内部类定义过多,让代码更加简洁。

  3. lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。 前提是函数式接口 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

  4. /*
    推导Lambda表达式
     */
    public class TestLambda {
    
        //3.静态内部类
        static class Like2 implements ILike {
            @Override
            public void Lambda() {
                System.out.println("i like lambda");
            }
    
            public static void main(String[] args) {
                ILike iLike = new Like();
                iLike.Lambda();
                iLike = new Like2();
                iLike.Lambda();
                //4.局部内部类
                 class Like3 implements ILike {
                    @Override
                    public void Lambda() {
                        System.out.println("i like lambda");
                    }
                }
                iLike=new Like3();
                 iLike.Lambda();
                 //5.匿名内部类,没有类的名称,必须借助接口或者父类
                iLike=new ILike() {
                    @Override
                    public void Lambda() {
                        System.out.println("i like lambda");
                    }
                };
                iLike.Lambda();
    
                //6.用Lambda简化
                iLike=()->{ System.out.println("i like lambda");};
                iLike.Lambda();
            }
        }
    
        //1.定义一个函数式接口
        interface ILike {
            void Lambda();
        }
    
        //2.实现类
        static class Like implements ILike {
            @Override
            public void Lambda() {
                System.out.println("i like lambda");
            }
        }
    }
public class TestLambda01 {
   
    public static void main(String[] args) {
        //1.lambda表示简化
        Ilove ilove=(int a)->{
            System.out.println("i  jj"+a);
        };
        //简化1.参数类型
        ilove=(a)->{
            System.out.println("i love you "+a);
        };
        //简化2.简化括号
        ilove=a->{
            System.out.println("i love you "+a);
        };
        //简化3.去掉花括号
        ilove=a-> System.out.println("i love you "+a);
        /*
          总结:
              lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。
              前提是函数式接口
              多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
         */
        ilove.love(521);
    }
}

interface Ilove{
    void love(int a);
}

静态代理

//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
/*
好处:代理对象可以做很多真实对象做不了的事情
      真实对象专注做自己的事情
 */
public class StaticProxy {
    public static void main(String[] args) {
        You you=new You();//你要结婚
        new Thread(()-> System.out.println("我爱你")).start();
        new WeddingCompany(new You()).HappMarry();
    }
}

interface Marry{
    void HappMarry();
}
//真实角色
class You implements Marry{
    @Override
    public void HappMarry() {
        System.out.println("你要结婚了,超开心");
    }
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
    //代理谁--》真实目标角色
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappMarry() {
        before();
        this.target.HappMarry();//这就是真实对象
        after();
    }

    private void after() {
        System.out.println("结婚之后,收尾款");
    }

    private void before() {
        System.out.println("结婚之前,布置现场");
    }
}

线程状态

停止线程

//测试stop
//1.建议线程正常停止--->利用次数,,不建议死循环
//2.建议使用标志位---->设置一个标志位
//3.不要使用stop或destroy等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{
    //1.设置一个标识位
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run...Thread"+i++);
        }
    }

    //2.设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag=false;
    }
    public static void main(String[] args) {
        TestStop testStop=new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("main"+i);
            if(i==900){
                //调用stop方法切换标志位,让线程停止
                testStop.stop();
                System.out.println("该线程停止了");
            }
        }
    }
}

线程休眠

  1. 每一个对象都有一个锁,sleep不会释放锁

  2. //模拟倒计时,,,
    public class TestSleep01 {
        public static void main(String[] args) {
            //打印当前系统时间
            Date date=new Date(System.currentTimeMillis());
            while (true){
                try {
                    System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                    Thread.sleep(1000);
                    date=new Date();//更新当前时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void tenDown() throws InterruptedException {
            int num=10;
            while (true){
                Thread.sleep(1000);
                System.out.println(num--);
                if(num<=0){
                    break;
                }
            }
        }
    }

线程礼让

将线程从运行状态转为就绪状态,礼让不一定成功,看cpu。

//测试礼让线程
//礼让不一定成功,看cpu心情
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield=new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}
class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

Join

Join合并线程,待此线程执行完成后,在执行其它线程,其它线程阻塞。

//测试Join方法,想象成插队
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100; i++) {
            System.out.println("线程vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        TestJoin testJoin=new TestJoin();
        Thread thread=new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 1000; i++) {
            if (i==200){
                thread.join();//插队
            }
            System.out.println("main"+i);
        }
    }
}

观测线程状态

线程中断或者结束,一旦进入死亡状态,就不能再次启动。

//观察测试线程的状态
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("///////");
        });

        //观察状态
        Thread.State state=thread.getState();
        System.out.println(state);//NEW

        //观察启动后
        thread.start();//启动线程
        state=thread.getState();
        System.out.println(state);//Run

        while (state!=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state=thread.getState();//更新线程状态
            System.out.println(state);//输出状态
        }
    }
}

线程优先级

优先级第只是意味着获得调度的概率低。

//测试线程的优先级
public class TestPriority {
    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        MyPriority myPriority=new MyPriority();
        Thread t1=new Thread(myPriority);
        Thread t2=new Thread(myPriority);
        Thread t3=new Thread(myPriority);
        Thread t4=new Thread(myPriority);
        Thread t5=new Thread(myPriority);

        //先设置优先级,再启动
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
        t4.start();

        t5.setPriority(8);
        t5.start();
    }
}

class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

守护线程

线程分为用户线程和守护线程,守护线程如后台记录操作日志,监控内存,垃圾回收等待。

//测试守护线程
//上帝守护你
public class TestDaemon {
    public static void main(String[] args) {
        God god=new God();
        You01 you01=new You01();
        Thread thread=new Thread(god);
        thread.setDaemon(true);//默认false表示是用户线程,正常的线程都是用户线程
        thread.start();//上帝守护线程启动
        new Thread(you01).start();//你  用户线程启动。。。
    }
}

//上帝
class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("上帝保佑着你");
        }
    }
}

//你
class You01 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("=========goodbye!world==============");
    }
}

线程同步

多个线程操作同一个资源。并发问题。

解决线程安全的形成条件:队列+锁

线程不安全的三个例子:

同步方法

//不安全的买票
//线程不安全,有负数
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket=new BuyTicket();
        new Thread(buyTicket,"我").start();
        new Thread(buyTicket,"老师").start();
        new Thread(buyTicket,"黄牛").start();
    }
}
class BuyTicket implements Runnable{
    //票
    private int ticketNums=10;
    boolean flag=true;
    @Override
    public void run() {
        //买票
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //synchronized 同步方法,锁的是this
    public synchronized void buy() throws InterruptedException {
        //判断是否有票
        if(ticketNums<=0){
            flag=false;
            return;
        }
        //模拟延迟
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
    }
}

同步块

//不安全的取钱
//两个人去银行取钱,账户
public class UnsafeBank {
   public static void main(String[] args) {
       Account account=new Account(100,"结婚基金");
       Drawing you=new Drawing(account,50,"你");
       Drawing girlFriend=new Drawing(account,100,"girlFriend");
       you.start();
       girlFriend.start();
  }
}

//账户
class Account{
   int money;//余额
   String name;//卡名

   public Account(int money, String name) {
       this.money = money;
       this.name = name;
  }
}

//银行:模拟取款
class Drawing extends Thread{
   Account account;//账户
   //取了多少钱
   int drawingMoney;
   //现在手里有多少钱
   int nowMoney;
   public Drawing(Account account,int drawingMoney,String name){
       super(name);
       this.account=account;
       this.drawingMoney=drawingMoney;
  }
   //取钱
   //synchronized 默认锁的是this
   @Override
   public void run() {
       //锁的对象就是变化的量,需要增删改
       synchronized (account){
           //判断有没有钱
           if(account.money-drawingMoney<0){
               System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
               return;
          }
           try {//sleep可以放大问题的发生性
               Thread.sleep(1000);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
           //卡内余额=余额-你取的钱
           account.money=account.money-drawingMoney;
           //你手里的钱
           nowMoney=nowMoney+drawingMoney;
           System.out.println(account.name+"余额为:"+account.money);
           //Thread.currentThread().getName()==this.getName()
           System.out.println(this.getName()+"手里的钱:"+nowMoney);
      }

  }
}
//线程不安全的集合
public class UnsafeList {
   public static void main(String[] args) {
       List<String> list=new ArrayList<>();
       for (int i = 0; i < 10000; i++) {
           new Thread(()->{
               synchronized (list){
                   list.add(Thread.currentThread().getName());
              }
          }).start();
      }
       try {
           Thread.sleep(3000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(list.size());
  }
}

CopyOnWriteArrayList

//测试JUC安全类型的集合
public class TestJUC {
   public static void main(String[] args) {
       CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
       for (int i = 0; i < 10000; i++) {
           new Thread(()->{
               list.add(Thread.currentThread().getName());
          }).start();
      }
       try {
           Thread.sleep(3000);
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
       System.out.println(list.size());
  }
}

死锁

  1. 死锁:多个线程互相抱着对方需要的资源,然后形成僵持。

  2. 产生死锁的四个必要条件:互斥条件:一个资源每次只能被一个进程使用。

    请求与保持条件:一个进程因请求资源而阻塞时,对以获得的资源保持不放。

    不剥夺条件:进程以获得的资源,在未使用完之前,不能强行剥夺。

    循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
   public static void main(String[] args) {
       Makeup g1=new Makeup(0,"灰姑凉");
       Makeup g2=new Makeup(1,"白雪公主");
       g1.start();
       g2.start();
  }
}
//口红
class Lipstick{

}

//镜子
class Mirror{

}

class Makeup extends Thread{
   //需要的资源只有一份,用static来保证只有一份
   static Lipstick lipstick=new Lipstick();
   static Mirror mirror=new Mirror();

   int choice;//选择
   String girlName;//使用化妆品的人

   Makeup(int choice,String girlName){
       this.choice=choice;
       this.girlName=girlName;
  }
   @Override
   public void run() {
       //化妆
       try {
           makeup();
      } catch (InterruptedException e) {
           e.printStackTrace();
      }
  }
   //化妆,互相持有对方的锁,就是需要拿到对方的资源
   private void makeup() throws InterruptedException {
       if(choice==0){
           synchronized(lipstick){//获得口红的锁
               System.out.println(this.girlName+"获得口红的锁");
               Thread.sleep(1000);
          }
           synchronized (mirror){//一秒钟后想获得镜子
               System.out.println(this.girlName+"获得镜子的锁");
          }
      }else{
           synchronized(mirror){//获得镜子的锁
               System.out.println(this.girlName+"获得镜子的锁");
               Thread.sleep(2000);
          }
           synchronized (lipstick){//二秒钟后想获得口红
               System.out.println(this.girlName+"获得口红的锁");
          }
      }
  }
}

Lock

ReentranLock类:可重入锁实现Lock接口

//测试Lock
public class TestLock {
  public static void main(String[] args) {
      TestLock2 testLock2=new TestLock2();
      new Thread(testLock2).start();
      new Thread(testLock2).start();
      new Thread(testLock2).start();
  }

}
class TestLock2 implements Runnable{
  int ticketNums=10;
  //定义Lock锁
  private final ReentrantLock lock=new ReentrantLock();
  @Override
  public void run() {
      while (true){
          try {
              lock.lock();//加锁
              if (ticketNums>0){
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.println(ticketNums--);
              }else{
                  break;
              }
          }finally {
              //解锁
              lock.unlock();
          }


      }
  }
}

synchronized与Lock的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘了关闭锁) synchronized是隐式锁,出了作用域自动释放。

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁。

  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。

  4. 优先使用顺序:Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)。

线程协作

管程法

//测试:生产者与消费者模型-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
   public static void main(String[] args) {
       SynContain synContain=new SynContain();
       new Productor(synContain).start();
       new Consumer(synContain).start();
  }
}

//生产者
class Productor extends Thread{
   SynContain contain;
   public Productor(SynContain contain){
       this.contain=contain;
  }
   //生产

   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           contain.push(new Chicken(i));
           System.out.println("生产了"+i+"只鸡");
      }
  }
}

//消费者
class Consumer extends Thread{
   SynContain contain;
   public Consumer(SynContain contain){
       this.contain=contain;
  }
   //消费

   @Override
   public void run() {
       for (int i = 0; i < 100; i++) {
           System.out.println("消费了-->"+contain.pop().id+"只鸡");
      }
  }
}

//产品
class Chicken extends Thread{
   int id;//产品编号
   public Chicken(int id){
       this.id=id;
  }
}

//缓冲区
class SynContain{
   //需要一个容器大小
   Chicken[] chickens=new Chicken[10];
   //容器计数器
   int count=0;
   //生产者放入产品
   public synchronized  void push(Chicken chicken){
       //如果容器满了,就需要等待消费者消费
       if(count==chickens.length){
           //通知消费者消费,生产等待
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       //如果没满,我们就需要丢入产品
       chickens[count]=chicken;
       count++;

       //可以通知消费者消费了,
       this.notifyAll();
  }

   //消费者消费产品
   public synchronized Chicken pop(){
       //判断能否消费
       if(count==0){
           //等待生产者生产,消费者等待
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       //如果可以消费
       count--;
       Chicken chicken=chickens[count];
       //吃完了,通知生产者生产
       this.notifyAll();
       return chicken;
  }

}

信号灯法

//测试生产者消费者问题2:信号灯法,标志位解决
public class TestPc2 {
   public static void main(String[] args) {
       TV tv=new TV();
       new Player(tv).start();
       new Watcher(tv).start();

  }
}
//生产者-->演员
class Player extends Thread{
   TV tv;
   public Player(TV tv){
       this.tv=tv;
  }

   @Override
   public void run() {
       for (int i = 0; i < 20; i++) {
           if(i%2==0){
               this.tv.play("快乐大本营播放中");
          }else{
               this.tv.play("抖音记录美好生活");
          }
      }
  }
}

//消费者-->观众
class Watcher extends Thread{
   TV tv;
   public Watcher(TV tv){
       this.tv=tv;
  }

   @Override
   public void run() {
       for (int i = 0; i < 20; i++) {
           tv.watch();
      }
  }
}

//产品-->节目
class TV{
   //演员表演,观众等待
   //观众观看,演员等待
   String voice;//表演的节目
   boolean flag=true;
   //表演
   public synchronized void play(String voice){
       if(!flag){
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       System.out.println("演员表演了:"+voice);
       //通知观众观看
       this.notifyAll();//通知唤醒
       this.voice=voice;
       this.flag=!this.flag;
  }
   //观看
   public synchronized void watch(){
       if(flag){
           try {
               this.wait();
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
       System.out.println("观看了:"+voice);
       //通知演员表演
       this.notifyAll();
       this.flag=!this.flag;
  }
}

线程池

  1. 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

//测试线程池
public class TestPool {
   public static void main(String[] args) {
       //1.创建服务,创建线程池
       //new FixedThreadPool 参数为:线程池大小
       ExecutorService service= Executors.newFixedThreadPool(10);
       //执行
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       service.execute(new MyThread());
       //2.关闭连接
       service.shutdownNow();
  }
}

class MyThread implements Runnable{
   @Override
   public void run() {
           System.out.println(Thread.currentThread().getName());
  }
}

集合

  1. 单列集合:Collection

    List:ArrayList,LinkedList,Vector

    Set:HashSet,TreeSet

  2. 双列集合:Map:HashMap,TreeMap,Hashtable,Properties

//Collections方法
public class CollectionMethod {
   public static void main(String[] args) {
       List list=new ArrayList();
       //add:添加单个元素
       list.add("jack");
       list.add(10);
       list.add(true);
       System.out.println("list="+list);
       //remove:删除指定元素
       //list.remove(0);//删除第一个元素
       list.remove("jack");//指定删除某个元素
       System.out.println("list="+list);
       //contains:查找元素是否存在
       System.out.println(list.contains("jack"));//false
       //size:获取元素个数
       System.out.println(list.size());//2
       //isEmpty:判断是否为空
       System.out.println(list.isEmpty());//false
       //clear:清空
       list.clear();
       //addAll:添加多个元素
       ArrayList list2 = new ArrayList();
       list2.add("hong");
       list2.add(12);
       list.addAll(list2);
       System.out.println("list="+list);
       //containsAll:查找多个元素是否都存在
       System.out.println(list.containsAll(list2));//true
       //removeAll:删除多个元素
       list.add("gx");
       list.removeAll(list2);
       System.out.println("list="+list);//gx
  }
}
 
posted @ 2022-07-08 09:22  你是魔鬼哦  阅读(74)  评论(0)    收藏  举报