Java保姆级入门

前言:C语言基础贼好的可以学完1.然后从7.类看
本文适合编程语言零基础,直接入门java的同学,不知道适不适合突击期末60分,打基础还是不错的

tips:编程三分学,七分练。感谢y总的教导

1. 变量、运算符、表达式、输入与输出

一、编写一个简单的Java程序–手速练习

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

二、语法基础

  1. 变量
    变量必须先定义,才可以使用。不能重名。
    变量定义的方式:
public class Main {
    public static void main(String[] args) {
        int a = 5;
        int b, c = a, d = 10 / 2;
    }
}

内置数据类型:

类型字节数举例
byte1123
short212345
int4123456789
long81234567891011L
float41.2F
double81.2,1.2D
boolean1true,flase
char2‘A’

常量:

使用final修饰:

final int N = 110;

类型转化:

  • 显示转化:int x = (int)'A';
  • 隐式转化:double x = 12, y = 4 * 3.3;

2.运算符

A = 10, B = 20

运算符描述实例
+把两个数相加A + B 将得到 30
-从第一个数中减去第二个数A - B 将得到 -10
*把两个数相乘A * B 将得到 200
/分子除以分母B / A 将得到 2
%取模运算符,向零整除后的余数,注意余数可能为负数B % A 将得到 0
++自增运算符A++:先取值后加1;++A:先加1后取值
--自减运算符A--:先取值后减1;--A:先减1后取值
+=第一个数加上第二个数A = A + B 可以简写为 A += B
-=第一个数减去第二个数A = A - B 可以简写为 A -= B
*=第一个数乘以第二个数A = A * B 可以简写为 A *= B
/=第一个数除以第二个数A = A / B 可以简写为 A /= B
%=第一个对第二个数取余数A = A % B 可以简写为 A %= B

3.表达式
整数的加减乘除四则运算:

public class Main {
    public static void main(String[] args) {
        int a = 6 + 3 * 4 / 2 - 2;

        System.out.println(a);

        int b = a * 10 + 5 / 2;

        System.out.println(b);

        System.out.println(23 * 56 - 78 / 3);
    }
}

浮点数(小数)的运算:

public class Main {
    public static void main(String[] args) {
        double x = 1.5, y = 3.2;

        System.out.println(x * y);
        System.out.println(x + y);
        System.out.println(x - y);
        System.out.println(x / y);
    }
}

整型变量的自增、自减:

public class Main {
    public static void main(String[] args) {
        int a = 1;
        int b = a ++ ;
        System.out.println(a + " " + b);

        int c = ++ a;
        System.out.println(a + " " + c);
    }
}

4.输入

方式1,效率较低,输入规模较小时使用。(输入数据量在1e5以内)

import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        String str = sc.next();  // 读入下一个字符串
        int x = sc.nextInt();  // 读入下一个整数
        float y = sc.nextFloat();  // 读入下一个单精度浮点数
        double z = sc.nextDouble();  // 读入下一个双精度浮点数
        String line = sc.nextLine();  // 读入下一行
    }
}

方式2,效率较高,输入规模较大时使用。(数据量大于1e5)注意需要抛异常。

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        System.out.println(str);
    }
}

5.输出
方式1,效率较低,输出规模较小时使用。

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println(123);  // 输出整数 + 换行
        System.out.println("Hello World");  // 输出字符串 + 换行
        System.out.print(123);  // 输出整数
        System.out.print("sutongruishishuaige\n");  // 输出字符串
        System.out.printf("%04d %.2f\n", 4, 123.456D);  // 格式化输出,float与double都用%f输出
    }
}

System.out.printf()中不同类型变量的输出格式:

(1) int:%d
(2) float: %f, 默认保留6位小数
(3) double: %f, 默认保留6位小数
(4) char: %c, 回车也是一个字符,用’\n’表示
(5) String: %s

方式2,效率较高,输出规模较大时使用。注意需要抛异常。(即throws Exception)

import java.io.BufferedWriter;
import java.io.OutputStreamWriter;

public class Main {
    public static void main(String[] args) throws Exception {
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        bw.write("Hello World\n");
        bw.flush();  // 需要手动刷新缓冲区
    }
}

笔记补充:(前面看懂就过)

public class Main{ //类 Main 大写
public static void main(String[] args) //函数 main 小写
{
System.out.println(“Hello World”);
}
}
1.变量
(1)java中变量没有赋值直接输出会报错
(2)内置数据类型
byte整数,只不过占一个字节 1byte(字节)=8bit(比特)
short整数,占两个字节
int整数,占四个字节
long整数,占八个字节,大概在10的八次方左右
float浮点数,四个字节,有效数字大概为6到7位
double浮点数,八个字节,有效数字大概为15到16位
(①什么是有效数字?比如1.234*10的五次方,有效数字是四位)
(②有效数字为6到7位。意味着前6、7位是精确值,后面的是不精确的近似值)
(③显示表示float,后面加f或F,显示表示double,后面加d或D)
boolen true or false
char 字符类型 (ASCII码对应关系: a-97 A-65 0-48)
(3)常量
变量可以修改,常量赋值之后不能再修改
常量是为了自己写代码,以及代码间的协作
java中的final相当于c中的const
(4)类型转化
显示转化:你可以明显的看到变量类型是怎么转换的过程
隐式转化:
double x=12,会先隐形的把12转化为double类型,然后再赋值给x
double y=43.3 ,会先把int型的4转化为double类型,然后3.3,然后再赋值给y
(为什么会先将int型转化为double类型?原则:优先将表示范围小的变量类型转化位表示范围大的变量类型)
int t=‘A’+2,会先把char型的字符A转化为对应int型的ACSII码,然后+2,然后赋值给t
(显示转化(强制转换)可以大转小,隐式转化只能小转大)
2.运算符
/:java和c中的/是向0取整,python中的/是向下取整
%:数学中的取模的结果只能是正数,但java和c中可以为负数
a 先取值再加1, a,先加1再取值
int a1=1,a2=1;
int b1=a1; //b1=1 a1=2
int b2=++a2; //b2=2 a2=2
3.表达式
()括号可以改变运算的优先级
浮点数的比较:
直接判断可能会有误差 if(x==y)
一般通过计算两个数的差的绝对值和一个很小的数进行判断来比较 if(Math.abs(x-y)<1e-8)
4.输入输出:
java中的单词命名都是驼峰命名法,两个单词的话第二个单词要大些
java的优势:生态好,便于多人协作,工具多包多,运行效率比python快
(1)输入1:
输入要利用工具Scanner,先要引入包 import java.util.Scanner;
next遇到空白字符就会输入结束(空格、回车、制表符等等)
nextLine可以读入一行字符串
比较慢的数据输入方式,如果输入的数在10的5次方以内可以用
(2)输入方式2:
输入数据量超过10的五次方,一般用BufferedReader
前面要加:
import java.io.BufferedReader;
import java.io.InputStreamReader;
注意抛出异常
BufferedWriter输入整数要手动输入

4.输入
输入方式2:
双引号表示字符串单引号表示字符
readLine只能读一行字符串,想要读整数的话,需要手动操作将字符串转化成int,如果有空格,需要手动先分割出来
String[] str = br.readLine().split(” “);
int x = Integer.parseInt(str[0]);
int y = Integer.parseInt(str[1]);
5.输出
(1)输出方式1:效率慢
println:输出自带一个回车,可以输出字符串、变量、数字等,不能格式化输出
print:输出最后不带回车,其他与println一样,不能格式化输出
printf:格式化输出,与c++中printf一样
(java中的double和float都是%f,不是%lf)
(%04,前面不足四位会补0,在输出年份是常用,%4d占4位,不足4位在前面补空格,%-4d,不足四位在后面补空格)
(2)输出方式2:(效率快)
注意最后要需要手动刷新缓冲区,如果不刷新有些时候会获取不到输出
bw.flush(); // 需要手动刷新缓冲区

2. 判断语句

C/C艹选手直接跳过

一、if 语句

1.基本if-else语句
当条件成立时,执行某些语句;否则执行另一些语句。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();

        if (a > 5) {
            System.out.printf("%d is big!\n", a);
            System.out.printf("%d + 1 = %d\n", a, a + 1);
        } else {
            System.out.printf("%d is small!\n", a);
            System.out.printf("%d - 1 = %d\n", a, a - 1);
        }
    }
}

else 语句可以省略:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();

        if (a > 5) {
            System.out.printf("%d is big!\n", a);
            System.out.printf("%d + 1 = %d\n", a, a + 1);
        }
    }
}

当只有一条语句时,大括号可以省略:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt();

        if (a > 5)
            System.out.printf("%d is big!\n", a);
        else
            System.out.printf("%d is small!\n", a);
    }
}

练习:输入一个整数,输出这个数的绝对值。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();

        if (x > 0)
            System.out.println(x);
        else
            System.out.println(-x);
    }
}

练习:输入两个整数,输出两个数中较大的那个。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(), b = sc.nextInt();

        if (a > b)
            System.out.println(a);
        else
            System.out.println(b);
    }
}

if-else语句内部也可以是if-else语句。

练习:输入三个整数,输出三个数中最大的那个。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();

        if (a > b) {
            if (a > c)
                System.out.println(a);
            else
                System.out.println(c);
        } else {
            if (b > c)
                System.out.println(b);
            else
                System.out.println(c);
        }
    }
}
  • 也可以用max函数(拓展)
import java.util.Scanner;

public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();
        System.out.printf("%d\n",Math.max(Math.max(a,b),c));
    }
}

2. 常用比较运算符

(1) 大于 >
(2) 小于 <
(3) 大于等于 >=
(4) 小于等于 <=
(5) 等于 ==
(6) 不等于 !=

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(), b = sc.nextInt();

        if (a > b) System.out.printf("%d > %d\n", a, b);
        if (a >= b) System.out.printf("%d >= %d\n", a, b);
        if (a < b) System.out.printf("%d < %d\n", a, b);
        if (a <= b) System.out.printf("%d <= %d\n", a, b);
        if (a == b) System.out.printf("%d == %d\n", a, b);
        if (a != b) System.out.printf("%d != %d\n", a, b);
    }
}

3. if-else连写:

输入一个0到100之间的分数,
如果大于等于85,输出A;
如果大于等于70并且小于85,输出B;
如果大于等于60并且小于70,输出C;
如果小于60,输出 D;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int s = sc.nextInt();

        if (s >= 85) {
            System.out.println("A");
        } else if (s >= 70) {
            System.out.println("B");
        } else if (s >= 60) {
            System.out.println("C");
        } else {
            System.out.println("D");
        }
    }
}

练习:

1.判断闰年。闰年有两种情况:
(1) 能被100整除时,必须能被400整除;
(2) 不能被100整除时,被4整除即可。
输入一个年份,如果是闰年输出yes,否则输出no

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int year = sc.nextInt();

        if (year % 100 == 0) {
            if (year % 400 == 0)
                System.out.println("yes");
            else
                System.out.println("no");
        } else {
            if (year % 4 == 0)
                System.out.println("yes");
            else
                System.out.println("no");
        }
    }
}

二、条件表达式

(1) 与 &&
(2) 或 ||
(3) 非 !

例题:输入三个数,输出三个数中的最大值。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int a = sc.nextInt(), b = sc.nextInt(), c = sc.nextInt();

        if (a >= b && a >= c)
            System.out.println(a);
        else if (b >= a && b >= c)
            System.out.println(b);
        else
            System.out.println(c);
    }
}

练习:用一条if语句,判断闰年。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int year = sc.nextInt();

        if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
            System.out.println("yes");
        else
            System.out.println("no");
    }
}

三、switch 语句

注意 swtich语句中如果不加break语句,则从上到下匹配到第一个case后,会顺次执行后面每个case中的语句。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int day = sc.nextInt();
        String name;

        switch(day) {
            case 1:
                name = "Monday";
                break;
            case 2:
                name = "Tuesday";
                break;
            case 3:
                name = "Wednesday";
                break;
            case 4:
                name = "Thursday";
                break;
            case 5:
                name = "Friday";
                break;
            case 6:
                name = "Saturday";
                break;
            case 7:
                name = "Sunday";
                break;
            default:
                name = "not valid";
        }

        System.out.println(name);
    }
}

笔记补充:(看懂就过)

1.工具:
(1)比较运算符(六个)
大于 >
小于 <
大于等于 >=
小于等于 <=
等于 ==
不等于 !=
(2)关系表达式(三个)
与 &&
或 ||
非 ! :取反的意思

扩展:
六种比较的方式其实用 a < b 这一种方式都可以凑出来
a > b 等价于 b < a
a>=b 等价于 !(a<b) 即a不小于b
a<=b 等价于 !(b<a) 即b不小于a
a==b 等价于 !(a<b)&&!(b<a) 类似于高数中的夹逼原理
a!=b 等价于 (a<b)||(b<a)
这也是为什么在c++中重载运算符时,只需要重载<小于号就可以了

2.判断语句:
(1)单分支if语句
(2)双分支if-else语句
①else语句可以不写,就是单份支if语句了。
②如果{}中只有一条语句的话,{}可以省略掉
③if-else语句中可以嵌套if-else语句
(3)多分支if-else-if语句
相当于if-else语句中嵌套if-else语句
(4)switch语句
类似于if-else语句连写的形式,只不过switch限制更多
switch每个case后最好要加break语句

3. 循环语句

C/C艹跳过

学习循环语句只需要抓住一点——代码执行顺序!

一、while循环

可以简单理解为循环版的if语句。if语句是判断一次,如果条件成立,则执行后面的语句;while是每次判断,如果成立,则执行循环体中的语句,否则停止。

public class Main {
    public static void main(String[] args) {
        int i = 0;
        while (i < 10) {
            System.out.println(i);
            i ++ ;
        }
    }
}

练习:求1~100中所有数的立方和。

public class Main {
    public static void main(String[] args) {
        int i = 1, sum = 0;
        while (i <= 100) {
            sum += i * i * i;
            i ++ ;
        }
        System.out.println(sum);
    }
}

练习:求斐波那契数列的第n项。f(1) = 1, f(2) = 1, f(3) = 2, f(n) = f(n-1) + f(n-2)

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        int a = 1, b = 1, i = 1;
        while (i < n) {
            int c = a + b;
            a = b;
            b = c;
            i ++ ;
        }

        System.out.println(a);
    }
}

死循环:循环永久执行,无法结束。我们要避免写出死循环。

public class Main {
    public static void main(String[] args) {
        int x = 1;
        while (x == 1)
            System.out.println("!");
    }
}

二、do while循环

do while循环不常用。
do while语句与while语句非常相似。唯一的区别是,do while语句限制性循环体后检查条件。不管条件的值如何,我们都要至少执行一次循环。

public class Main {
    public static void main(String[] args) {
        int x = 1;
        while (x < 1) {
            System.out.println("x!");
        }

        int y = 1;
        do {
            System.out.println("y!");
        } while (y < 1);
    }
}

三、for循环

基本思想:把控制循环次数的变量从循环体中剥离。

for (init-statement; condition; expression) {
    statement
}

init-statement可以是声明语句、表达式、空语句,一般用来初始化循环变量;
condition是条件表达式,和while中的条件表达式作用一样;可以为空,空语句表示true
expression一般负责修改循环变量,可以为空。

public class Main {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i ++ ) {  // 循环体中只有一条语句时,可以不加大括号
            System.out.println(i);
        }
    }
}

练习:求1~100中所有数的立方和。

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i <= 100; i ++ )
            sum += i * i * i;
        System.out.println(sum);
    }
}

练习:求斐波那契数列的第n项。f(1) = 1, f(2) = 1, f(3) = 2, f(n) = f(n-1) + f(n-2)

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        int a = 1, b = 1;
        for (int i = 1; i < n; i ++ ) {
            int c = a + b;
            a = b;
            b = c;
        }

        System.out.println(a);
    }
}

init-statement可以定义多个变量,expression也可以修改多个变量。

例如求 1 * 10 + 2 * 9 + 3 * 8 + 4 * 7 + 5 * 6

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1, j = 10; i < j; i ++, j -- ) {
            sum += i * j;
        }

        System.out.println(sum);
    }
}

四、跳转语句

1.break
可以提前从循环中退出,一般与if语句搭配。
例题:判断一个大于1的数是否是质数:

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();

        boolean isPrime = true;
        for (int i = 2; i < n; i ++ )
            if (n % i == 0) {
                isPrime = false;
                break;
            }

        if (isPrime)
            System.out.println("yes");
        else
            System.out.println("no");
    }
}

2. continue

可以直接跳到当前循环体的结尾。作用与if语句类似。
例题:求1~100中所有偶数的和。

public class Main {
    public static void main(String[] args) {
        int sum = 0;

        for (int i = 1; i <= 100; i ++ ) {
            if (i % 2 == 1) continue;
            sum += i;
        }

        System.out.println(sum);
    }
}

五、多层循环

将1~100打印到一个10 * 10的矩阵中:

public class Main {
    public static void main(String[] args) {
        for (int i = 0, k = 1; i < 10; i ++ ) {
            for (int j = 0; j < 10; j ++, k ++ ) {
                System.out.printf("%d ", k);
            }
            System.out.println();
        }
    }
}

笔记补充:

基本数据类型判断相等用==比较,其他的判相等(比如string、函数)都要用equals来比较

循环语句:
学习循环一定要理清循环的顺序
1.while循环
相当于循环版的if语句。先判断再执行循环语句
2.do-while循环
先执行循环语句,再判断。所以do-while循环会至少执行一次。

只有当第一次循环条件不成立时,while和do-while才有区别,其他情况下这两个都等价:
第一次循环条件如果不成立,while循环直接进不去不成立,而do-while循环会执行一次

3.for循环
把控制循环次数的变量从循环体中剥离。
for(循环变量初始化;条件表达式;修改循环变量)
{
循环体;
}

4.跳转语句
(1)break:跳出这层循环
(2)continue:跳出当前这次循环

5.循环嵌套

4. 数组

一、 一维数组

1.1 数组的定义

数组的定义方式和变量类似。

public class Main {
    public static void main(String[] args) {
        int[] a = new int[10], b;
        float[] f = new float[33];
        double[] d = new double[123];
        char[] c = new char[21];
    }
}

1.2 数组的初始化

public class Main {
    public static void main(String[] args) {
        int[] a = {0, 1, 2};        // 含有3个元素的数组,元素分别是0, 1, 2
        int[] b = new int[3];       // 含有3个元素的数组,元素的值均为0
        char[] d = {'a', 'b', 'c'}; // 字符数组的初始化
    }
}

1.3 访问数组元素

通过下标访问数组。

public class Main {
    public static void main(String[] args) {
        int[] a = {0, 1, 2};  // 数组下标从0开始

        System.out.printf("%d %d %d\n", a[0], a[1], a[2]);

        a[0] = 5;

        System.out.println(a[0]);
    }
}

二、 多维数组

多维数组就是数组的数组。

public class Main {
    public static void main(String[] args) {
        int[][] a = new int[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组。
        int[][][] b = new int[10][20][30]; // 将所有元素的初值为0
        // 大小为10的数组,它的每个元素是含有20个数组的数组
        // 这些数组的元素是含有30个整数的数组
    }
}
public class Main {
    public static void main(String[] args) {
        int[][] a = {           // 三个元素,每个元素都是大小为4的数组
            {0, 1, 2, 3},       // 第1行的初始值
            {4, 5, 6, 7},       // 第2行的初始值
            {8, 9, 10, 11}      // 第3行的初始值
        };


        for (int i = 0; i < 4; i ++ )  // 将第一行全部变成0
            a[0][i] = 0;

        for (int i = 0; i < 3; i ++ ) {  // 输出二维数组
            for (int j = 0; j < 4; j ++ ) {
                System.out.printf("%d ", a[i][j]);
            }
            System.out.println();
        }
    }
}

三、 数组的范围遍历

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        int[][] a = {
            {0, 1, 2, 3},
            {4, 5, 6, 7},
            {8, 9, 10, 11},
        };

        for (int[] row: a) {  // 范围遍历
            for (int x: row)  // 范围遍历
                System.out.printf("%d ", x);
            System.out.println();
        }
    }
}

四、 常用API

与C艹STL类似

属性length:返回数组长度,注意不加小括号
Arrays.sort():数组排序
Arrays.fill(int[] a, int val):填充数组
Arrays.toString():将数组转化为字符串
Arrays.deepToString():将多维数组转化为字符串
数组不可变长
使用Arrays需要import java.util.Arrays

笔记补充:

数组就是方便我们定义一大堆变量的工具。
1.一维数组

1.1数组的定义:
(1)java中数组的定义[]是写在数组名前面(与c++区分),开辟长度需要new,即面向对象
int[] a;//定义:定义一个数组a
a = new int[10]; //初始化:将数组a初始化为一个长度为10的数组,表示a里面包含10个int变量

等价于=>int[] a = new int[10]//边定义边初始化
(2)也可以同时定义多个数组
int[] a = new int[10], b;
(3)不一定只要基本数据类型可以定义数组,String也可以定义数组
String[] a = new String[10];//这个数组里面包含10个String类型的变量

1.2数组的初始化
(1)默认初始化:int[] a = new int[3]; // 含有3个元素的数组,元素的值均为0

①java中初始化默认的都是0,然后通过相应的类型转为不同的0
(c++中局部变量数组初始化默认都是随机值,不确定的。)
int类型定义的数组,初始化默认是0
String类型定义的数组,默认值是null
char类型定义的数组,使用UTF8字符集 给出的结果是0
double类型定义的数组,默认值是0.0
loat类型定义的数组,默认值是0.0
boolean类型定义的数组,默认值是false

②int[] b; //b也是一个数组,没有初始化就是一个空数组。
[]是数组类型,java中把[]写到前面是把所有的数组类型都写到前面,后面定义的都是数组。
(而c++中是int b[10])

(2)直接初始化:
int[] a = {0, 1, 2}; // 含有3个元素的数组,元素分别是0, 1, 2
①java中的数组是可以重新赋值的,其实就是一个变量,跟普通变量没有区别。
int[] a = {0, 1, 2};
a = new int[10];
②java中没有指针的概念
③java中的数组如果不初始化是不能用的,是一个空数组null。必须先赋初值后使用
变量也是一样的,如果不赋初值也不能用。
所以java不容易出错。

1.3数组的访问
下标从0开始
(1)取值:a[0]
(2)赋值:a[0] = 5;

补充:
①java中数组长度可以是变量,而c++中最好是常量
int[] f = new int[n+1];

2.多维数组
多维数组就是数组的数组,即数组里面的元素还是数组
2.1多维数组的定义
int[][] a = new int[3][4]; // 大小为3的数组,每个元素是含有4个整数的数组。
int[][][] b = new int[10][20][30]; // 大小为10的数组,它的每个元素是含有20个数组的数组,这些数组的元素是含有30个整数的数组
2.2多维数组的初始化
int[][] a = { // 三个元素,每个元素都是大小为4的数组
{0, 1, 2, 3}, // 第1行的初始值
{4, 5, 6, 7}, // 第2行的初始值
{8, 9, 10, 11} // 第3行的初始值
};
2.3多维数组的访问
a[i][j]; //第i行第j个元素

扩充:
①二维数组中每一维的长度可以是不一样的,但一般是一样的。

②属性length:返回数组长度,注意不加小括号
(这个不是API)
a.length; //返回数组a的长度
a[i].length; //如果a是二维数组,返回每一维数组的长度

3.常用API
使用Arrays需要import java.util.Arrays

(1)Arrays.sort(q):将数组q中的元素从小到大排序
要想实现从大到小排序里面要使用匿名函数(类似Python的Lambda表达式),并且数组要用对象定义的方式
①一维数组:
Integer[] q= new Integer[n]; //Integer是对象类型的整数
Arrays.sort(q, (x,y) ->{ //加一个匿名函数
return x-y; // x-y从小到大排序,y-x从大到小排序
} )
②二维数组:
二维数组中的每一个一维数组本身就是一个对象,所以他不需要用Integer
Arrays.sort(q, (x,y) ->{ //加一个匿名函数
return x[0]-y[0];
} )

(2)Arrays.toString(q):将数组q转化为字符串展开
(3)Arrays.deepToString(q):将多维数组q转化为字符串展开
(4)Arrays.fill(int[] q, int val):填充数组,只能初始化一维数组,不能初始化多维数组
例:Arrays.fill(q, -1)

4.数组的范围遍历:
for(数据类型 变量名:数组名)
{
这个变量名就代表数组中的每一项元素了,直接使用变量名进行操作
}

5. 字符串

一、字符与整数的联系——ASCII码

每个常用字符都对应一个-128 ~ 127的数字,二者之间可以相互转化。注意:目前负数没有与之对应的字符。

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        char c = 'a';
        System.out.println((int)c);

        int a = 66;
        System.out.println((char)a);
    }
}

常用ASCII值:'A'- 'Z'65 ~ 90'a' - 'z'97 - 1220 - 948 - 57
字符可以参与运算,运算时会将其当做整数:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int a = 'B' - 'A';
        int b = 'A' * 'B';
        char c = 'A' + 2;

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }
}

二、 String类

初始化:

String a = "Hello World";
String b = "My name is ";
String x = b;  // 存储到了相同地址
String c = b + "yxc";  // String可以通过加号拼接
String d = "My age is " + 18;  // int会被隐式转化成字符串"18"
String str = String.format("My age is %d", 18);  // 格式化字符串,类似于C++中的sprintf
String money_str = "123.45";
double money = Double.parseDouble(money_str);  // String转double

只读变量,不能修改,例如:

String a = "Hello ";
a += "World";  // 会构造一个新的字符串

访问String中的字符:

String str = "Hello World";
for (int i = 0; i < str.length(); i ++ ) {
    System.out.print(str.charAt(i));  // 只能读取,不能写入
}

三、常用API:

  • length():返回长度

  • split(String regex):分割字符串

  • indexOf(char c)indexOf(String str)lastIndexOf(char c)lastIndexOf(String str):查找,找不到返回-1

  • equals():判断两个字符串是否相等,注意不能直接用==

  • compareTo():判断两个字符串的字典序大小,负数表示小于,0表示相等,正数表示大于

  • startsWith():判断是否以某个前缀开头

  • endsWith():判断是否以某个后缀结尾

  • trim():去掉首尾的空白字符

  • toLowerCase():全部用小写字符

  • toUpperCase():全部用大写字符

  • replace(char oldChar, char newChar):替换字符

  • replace(String oldRegex, String newRegex):替换字符串

  • substring(int beginIndex, int endIndex):返回(beginIndex, endIndex)中的子串

  • toCharArray():将字符串转化成字符数组

四、输入与输出

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str1 = sc.next();  // 输入字符串,遇到空格、回车等空白字符时停止输入
        String str2 = sc.nextLine();  // 输入一整行字符串,遇到空格不会停止输入,遇到回车才会停止

        System.out.println(str1);  // 可以直接输出
        System.out.printf("%s\n", str2);  // 也可以格式化输出,用 %s 表示字符串
    }

StringBuilderStringBuffer
String不能被修改,如果打算修改字符串,可以使用StringBuilderStringBuffer

StringBuffer线程安全,速度较慢;StringBuilder线程不安全,速度较快。

StringBuilder sb = new StringBuilder("Hello ");  // 初始化
sb.append("World");  // 拼接字符串
System.out.println(sb);

for (int i = 0; i < sb.length(); i ++ ) {
    sb.setCharAt(i, (char)(sb.charAt(i) + 1));  // 读取和写入字符
}

System.out.println(sb);

常用API:

  • reverse():翻转字符串

6.函数

一、函数基础

一个典型的函数定义包括以下部分:修饰符、返回类型、函数名字、由0个或多个形参组成的列表以及函数体。

1.1 编写函数

public class Main {
    private static int fact(int val) {
        int res = 1;
        for (int i = 1; i <= val; i ++ )
            res *= i;
        return res;
    }
}

函数名字是fact,它作用于一个整型参数,返回一个整型值。return语句负责结束fact并返回res的值。
修饰符包括private、static等,它们属于类相关的概念,会在下一章解释。

1.2 调用函数

public class Main {
    private static int fact(int val) {
        int res = 1;
        for (int i = 1; i <= val; i ++ )
            res *= i;
        return res;
    }

    public static void main(String[] args) {
        int res = fact(5);
        System.out.printf("5! is %d\n", res);
    }
}

函数的调用完成两项工作:一是用实参初始化函数对应的形参,二是将控制权转移给被调用函数。此时,主调函数的执行被暂时中断,被调函数开始执行。

1.3 形参和实参

实参是形参的初始值。第一个实参初始化第一个形参,第二个实参初始化第二个形参,依次类推。形参和实参的类型和个数必须匹配。

fact("hello");      // 错误:实参类型不正确
fact();             // 错误:实参数量不足
fact(42, 10, 0);    // 错误:实参数量过多
fact(' ');      // 正确:该实参能自动转换成int类型,' '的ASCII值为32,所以该操作等价于fact(32);

1.4 函数的形参列表

函数的形参列表可以为空,但是不能省略。

void f1() {/* …. */}            // 空形参列表

形参列表中的形参通常用逗号隔开,其中每个形参都是含有一个声明符的声明。即使两个形参的类型一样,也必须把两个类型都写出来:

int f3(int v1, v2) {/* … */}        // 错误
int f4(int v1, int v2) {/* … */}    // 正确

1.5 函数返回类型

大多数类型都能用作函数的返回类型。一种特殊的返回类型是void,它表示函数不返回任何值。
函数的返回类型也可以是数组、字符串或者其他对象:

import java.util.Arrays;

public class Main {
    private static int[] newArray() {
        int[] a = {1, 2, 3};
        return a;
    }

    private static String newString() {
        return "Hello World";
    }

    public static void main(String[] args) {
        System.out.println(Arrays.toString(newArray()));
        System.out.println(newString());
    }
}

1.6 变量的作用域

本章中我们只使用静态成员变量和静态成员函数,非静态成员变量/函数及其区别会在下一章中介绍。

函数内定义的变量为局部变量,只能在函数内部使用。
定义在类中的变量为成员变量,可以在类的所有成员函数中调用。
当局部变量与全局变量重名时,会优先使用局部变量。

public class Main {
    private static int x = 4;

    private static void f1() {
        int x = 3;
        System.out.println(x);
    }

    private static void f2() {
        System.out.println(x);
    }

    private static void f3() {
        System.out.println(x + 1);
    }

    public static void main(String[] args) {
        f1();
        f2();
        f3();
    }
}

二、参数传递

2.1 值传递

八大基本数据类型和String类型等采用值传递。

将实参的初始值拷贝给形参。此时,对形参的改动不会影响实参的初始值。

public class Main {
    private static void f(int x) {
        x = 5;
    }

    public static void main(String[] args) {
        int x = 10;
        f(x);
        System.out.println(x);
    }
}

2.2 引用传递

String以外的数据类型的对象,例如数组、StringBuilder等采用引用传递。

将实参的引用(地址)传给形参,通过引用找到变量的真正地址,然后对地址中的值修改。所以此时对形参的修改会影响实参的初始值。

import java.util.Arrays;

public class Main {
    private static void f1(int[] a) {
        for (int i = 0, j = a.length - 1; i < j; i ++, j -- ) {
            int t = a[i];
            a[i] = a[j];
            a[j] = t;
        }
    }

    private static void f2(StringBuilder sb) {
        sb.append("Hello World");
    }

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5};
        f1(a);
        System.out.println(Arrays.toString(a));

        StringBuilder sb = new StringBuilder("");
        f2(sb);
        System.out.println(sb);
    }
}

三、返回类型和return语句

return语句终止当前正在执行的函数并将控制权返回到调用该函数的地方。return语句有两种形式:

return;
return expression;

3.1 无返回值函数

没有返回值的return语句只能用在返回类型是void的函数中。返回void的函数不要求非得有return语句,因为在这类函数的最后一句后面会隐式地执行return

通常情况下,void函数如果想在它的中间位置提前退出,可以使用return语句。return的这种用法有点类似于我们用break语句退出循环。

public class Main {
    private static void swap(int[] a) {  // 交换a[0]和a[1]
        // 如果两个值相等,则不需要交换,直接退出
        if (a[0] == a[1])
            return;
        // 如果程序执行到了这里,说明还需要继续完成某些功能

        int tmp = a[0];
        a[0] = a[1];
        a[1] = tmp;
        // 此处无须显示的return语句
    }

    public static void main(String[] args) {
        int[] a = {3, 4};
        swap(a);
        System.out.printf("%d %d\n", a[0], a[1]);
    }
}

3.2 有返回值的函数

只要函数的返回类型不是void,则该函数内的每个分支都必须有return语句,且每条return语句都必须返回一个值。return语句返回值的类型必须与函数的返回类型相同,或者能隐式地转换函数的返回类型。

import java.util.Scanner;

public class Main {
    private static int max(int a, int b) {
        if (a > b)
            return a;
        return b;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt(), y = sc.nextInt();

        System.out.println(max(x, y));
    }
}

四、函数重载

函数重载是指:在同一个类中存在多个函数,函数名称相同但参数列表不同。

编译器会根据实参的类型选择最匹配的函数来执行。

import java.util.Scanner;

public class Main {
    private static int max(int a, int b) {
        System.out.println("int max");
        if (a > b) return a;
        return b;
    }

    private static double max(double a, double b) {
        System.out.println("double max");
        if (a > b) return a;
        return b;
    }

    public static void main(String[] args) {
        System.out.println(max(3, 4));
        System.out.println(max(3.0, 4.0));
    }
}

五、函数递归

在一个函数内部,也可以调用函数本身。

import java.util.Scanner;

public class Main {
    private static int fib(int n) {  // 求斐波那切数列第n项
        if (n <= 2) return 1;
        return fib(n - 1) + fib(n - 2);
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        System.out.println(fib(n));
    }
}

7.类与接口

7.1 类与对象

类定义一种全新的数据类型,包含一组变量和函数;对象是类这种类型对应的实例。
例如在一间教室中,可以将Student定义成类,表示“学生”这个抽象的概念。那么每个同学就是Student类的一个对象(实例)。

7.1.1 源文件声明规则

  • 一个源文件中只能有一个public类。
  • 一个源文件可以有多个非public类。
  • 源文件的名称应该和public类的类名保持一致。
  • 每个源文件中,先写package语句,再写import语句,最后定义类。

7.1.2 类的定义

  • public: 所有对象均可以访问
  • private: 只有本类内部可以访问
  • protected:同一个包或者子类中可以访问
  • 不添加修饰符:在同一个包中可以访问
  • 静态(带static修饰符)成员变量/函数与普通成员变量/函数的区别:
  • 所有static成员变量/函数在类中只有一份,被所有类的对象共享;
  • 所有普通成员变量/函数在类的每个对象中都有独立的一份;
  • 静态函数中只能调用静态函数/变量;普通函数中既可以调用普通函数/变量,也可以调用静态函数/变量。
class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public String toString() {
        return String.format("(%d, %d)", x, y);
    }
}

7.1.3 类的继承

每个类只能继承一个类。

class ColorPoint extends Point {
    private String color;

    public ColorPoint(int x, int y, String color) {
        super(x, y);
        this.color = color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String toString() {
        return String.format("(%d, %d, %s)", super.getX(), super.getY(), this.color);
    }
}

7.1.4 类的多态

public class Main {
    public static void main(String[] args) {
        Point point = new Point(3, 4);
        Point colorPoint = new ColorPoint(1, 2, "red");

        // 多态,同一个类的实例,调用相同的函数,运行结果不同
        System.out.println(point.toString());
        System.out.println(colorPoint.toString());
    }
}

7.2 接口

interfaceclass类似。主要用来定义类中所需包含的函数。

接口也可以继承其他接口,一个类可以实现多个接口。

7.2.1 接口的定义

接口中不添加修饰符时,默认为public

interface Role {
    public void greet();
    public void move();
    public int getSpeed();
}

7.2.2 接口的继承

每个接口可以继承多个接口

interface Hero extends Role {
    public void attack();
}

7.2.3 接口的实现

每个类可以实现多个接口

class Zeus implements Hero {
    private final String name = "Zeus";
    public void attack() {
        System.out.println(name + ": Attack!");
    }

    public void greet() {
        System.out.println(name + ": Hi!");
    }

    public void move() {
        System.out.println(name + ": Move!");
    }

    public int getSpeed() {
        return 10;
    }
}

7.2.4 接口的多态

class Athena implements Hero {
    private final String name = "Athena";
    public void attack() {
        System.out.println(name + ": Attack!!!");
    }

    public void greet() {
        System.out.println(name + ": Hi!!!");
    }

    public void move() {
        System.out.println(name + ": Move!!!");
    }

    public int getSpeed() {
        return 10;
    }
}

public class Main {
    public static void main(String[] args) {
        Hero[] heros = {new Zeus(), new Athena()};
        for (Hero hero: heros) {
            hero.greet();
        }
    }
}

8. 常用容器

跟C艹STL太像了,不要混淆

8.1 List

接口:java.util.List<>

实现:

java.util.ArrayList<>:变长数组
java.util.LinkedList<>:双链表
函数:

add():在末尾添加一个元素
clear():清空
size():返回长度
isEmpty():是否为空
get(i):获取第i个元素
set(i, val):将第i个元素设置为val

8.2 栈

类:java.util.Stack<>

函数:

push():压入元素
pop():弹出栈顶元素,并返回栈顶元素
peek():返回栈顶元素
size():返回长度
empty():栈是否为空
clear():清空

8.3 队列

接口:java.util.Queue<>

实现:

java.util.LinkedList<>:双链表
java.util.PriorityQueue<>:优先队列
默认是小根堆,大根堆写法:new PriorityQueue<>(Collections.reverseOrder())
函数:

add():在队尾添加元素
remove():删除并返回队头
isEmpty():是否为空
size():返回长度
peek():返回队头
clear():清空

8.4 Set

接口:java.util.Set<K>

实现:

  • java.util.HashSet<K>:哈希表
  • java.util.TreeSet<K>:平衡树

函数:

add():添加元素
contains():是否包含某个元素
remove():删除元素
size():返回元素数
isEmpty():是否为空
clear():清空
java.util.TreeSet多的函数:

ceiling(key):返回大于等于key的最小元素,不存在则返回null
floor(key):返回小于等于key的最大元素,不存在则返回null

8.5 Map

接口:java.util.Map<K, V>

实现:

java.util.HashMap<K, V>:哈希表
java.util.TreeMap<K, V>:平衡树
函数:

put(key, value):添加关键字和其对应的值
get(key):返回关键字对应的值
containsKey(key):是否包含关键字
remove(key):删除关键字
size():返回元素数
isEmpty():是否为空
clear():清空
entrySet():获取Map中的所有对象的集合
Map.Entry<K, V>:Map中的对象类型
getKey():获取关键字 getValue():获取值
java.util.TreeMap<K, V>多的函数:

ceilingEntry(key):返回大于等于key的最小元素,不存在则返回null
floorEntry(key):返回小于等于key的最大元素,不存在则返回null

9. 异常处理

1. Error与Exception的区别

Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。

Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。

2. Exception类的继承关系

exception.png

3. 运行时异常和非运行时异常的区别

运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等, 这些异常是非检查型异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,这些是检查型异常。一般情况下不自定义检查型异常。

4. 内置异常类

非检查性异常

异常描述
ArithmeticException当出现异常的运算条件时,抛出此异常。例如,一个整数”除以零”时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException当应用程序试图在需要对象的地方使用 null 时,抛出该异常。
NumberFormatException当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException当不支持请求的操作时,抛出该异常。

5. 内置异常方法

方法说明
public String getMessage()返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
public Throwable getCause()返回一个 Throwable 对象代表异常原因。
public String toString()返回此 Throwable 的简短描述。
public void printStackTrace()将此 Throwable 及其回溯打印到标准错误流
public StackTraceElement [] getStackTrace()返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
public Throwable fillInStackTrace()用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。

6. 捕获异常

import java.util.Scanner;

public class Main {

    private static void foo() {
        int[] array = new int[5];
        for (int i = 0; i < 5; i ++ )
            array[i] = i;

        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        int x = sc.nextInt();

        try {
            array[k] /= x;
        } catch (ArithmeticException e) {
            System.out.println("除零错误!");
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界!");
            e.printStackTrace();
        } finally {
            for (int i = 0; i < 5; i ++ ) {
                System.out.println(array[i]);
            }
        }
    }

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

7. 抛出异常

throw: 在函数内抛出一个异常。
throws:在函数定义时抛出一些可能的异常。

检查型异常必须被捕获或者抛出。

import java.io.IOException;
import java.util.Scanner;

public class Main {

    private static void foo() throws IOException, NoSuchFieldException {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        if (x == 1)
            throw new IOException("找不到文件!!!");
        else
            throw new NoSuchFieldException("自定义异常");
    }

    public static void main(String[] args) {
        try {
            foo();
        } catch (IOException e) {
            System.out.println("IOException!");
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            System.out.println("NoSuchFieldException!");
            e.printStackTrace();
        }
    }
}

8. try-with-resources

JDK7 之后,Java 新增的 try-with-resource 语法糖来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try 用于声明和实例化资源,catch 用于处理关闭资源时可能引发的所有异常。

import java.io.*;

public class Main {

    public static void main(String[] args) {
        String line;
        try (
                BufferedReader br = new BufferedReader(new FileReader("input.txt"));
                BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
        ) {
            while ((line = br.readLine()) != null) {
                System.out.println("Line => " + line);
                bw.write("copy: " + line + "\n");
            }
            bw.flush();
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        }
    }
}

10. 注解与反射

10.1 注解

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

10.1.1 常用注解

(1) @Override: 限定某个函数必须重载其他函数,该注解只能用于函数
(2) @Deprecated:用于表示某个程序元素(类、函数)已过时
(3) @SuppressWarnings:抑制编译器警告

10.1.2 元注解

修饰其他注解的注解,就被称为元注解。

(1) Retention:指定注解的作用范围
(2) Target:指定注解可以用在哪些地方
(3) Document:注定注解是否出出现在javadoc中
(4) Inherited:子类会继承父类的注解

10.2 反射

反射:动态引入类、动态调用实例的成员函数、成员变量等。

10.2.1 常用API

(1) java.lang.Class
(2) java.lang.reflect.Method
(3) java.lang.reflect.Field
(4) java.lang.reflect.Constructor

package org.yxc;

public class Calculator {
    public String name;

    public Calculator() {}

    public Calculator(String name) {
        this.name = name;
    }

    public int add(int a, int b) {
        return a + b;
    }
}
package org.yxc;

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

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        Class<?> cls = Class.forName("org.yxc.Calculator");
        Object o = cls.newInstance();

        Method method = cls.getMethod("add", int.class, int.class);
        int res = (int)method.invoke(o, 3, 4);
        System.out.println(res);

        Field field = cls.getField("name");
        field.set(o, "My Calculator!");
        System.out.println(field.get(o));

        Constructor<?> constructor = cls.getConstructor(String.class);
        Object new_o = constructor.newInstance("New Calculator!");
        System.out.println(new_o);
    }
}

10.2.2 优缺点

优点:可以动态创建和使用对象,使用灵活
缺点:执行速度慢

11. 多线程与锁

11.1 多线程

11.1.1 实现多线程

写法1:继承Thread类

class Worker extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i ++ ) {
            System.out.println("Hello! " + this.getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Worker worker1 = new Worker();
        Worker worker2 = new Worker();
        worker1.setName("thread-1");
        worker2.setName("thread-2");
        worker1.start();
        worker2.start();
    }
}

写法2:实现Runnable接口

class Worker1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i ++ ) {
            System.out.println("Hello! " + "thread-1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class Worker2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i ++ ) {
            System.out.println("Hello! " + "thread-2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new Thread(new Worker1()).start();
        new Thread(new Worker2()).start();
    }
}

11.1.2 常用API

start():开启一个线程
Thread.sleep(): 休眠一个线程
join():等待线程执行结束
interrupt():从休眠中中断线程
setDaemon():将线程设置为守护线程。当只剩下守护线程时,程序自动退出

11.2 锁

  • lock:获取锁,如果锁已经被其他线程获取,则阻塞
  • unlock:释放锁,并唤醒被该锁阻塞的其他线程
import java.util.concurrent.locks.ReentrantLock;
class Worker extends Thread {
    public static int cnt = 0;
    private static final ReentrantLock lock = new ReentrantLock();


    @Override
    public void run() {
        for (int i = 0; i < 100000; i ++ ) {
            lock.lock();
            try {
                cnt ++ ;
            } finally {
                lock.unlock();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Worker worker1 = new Worker();
        Worker worker2 = new Worker();

        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();

        System.out.println(Worker.cnt);
    }
}

11.3 同步(Synchronized)

写法1:将Synchronized加到代码块上

class Count {
    public int cnt = 0;
}

class Worker extends Thread {
    public final Count count;

    public Worker(Count count) {
        this.count = count;
    }

    @Override
    public void run() {
        synchronized (count) {
            for (int i = 0; i < 100000; i ++ ) {
                count.cnt ++ ;
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Count count = new Count();

        Worker worker1 = new Worker(count);
        Worker worker2 = new Worker(count);

        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();

        System.out.println(count.cnt);
    }
}

写法2:将Synchronized加到函数上(锁加到了this对象上)

class Worker implements Runnable {
    public static int cnt = 0;

    private synchronized void work() {
        for (int i = 0; i < 100000; i ++ ) {
            cnt ++ ;
        }
    }

    @Override
    public void run() {
        work();
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Worker worker = new Worker();
        Thread worker1 = new Thread(worker);
        Thread worker2 = new Thread(worker);

        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();

        System.out.println(Worker.cnt);
    }
}

11.3.1 wait与notify

package org.yxc;

class Worker extends Thread {
    private final Object object;
    private final boolean needWait;

    public Worker(Object object, boolean needWait) {
        this.object = object;
        this.needWait = needWait;
    }

    @Override
    public void run() {
        synchronized (object) {
            try {
                if (needWait) {
                    object.wait();
                    System.out.println(this.getName() + ": 被唤醒啦!");
                } else {
                    object.notifyAll();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        for (int i = 0; i < 5; i ++ ) {
            Worker worker = new Worker(object, true);
            worker.setName("thread-" + i);
            worker.start();
        }

        Worker worker = new Worker(object, false);
        worker.setName("thread-" + 5);
        Thread.sleep(1000);
        worker.start();
    }
}

完结撒花!!

感谢观看

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值