前言:C语言基础贼好的可以学完1.然后从7.类看
本文适合编程语言零基础,直接入门java的同学,不知道适不适合突击期末60分,打基础还是不错的
tips:编程三分学,七分练。感谢y总的教导
1. 变量、运算符、表达式、输入与输出
一、编写一个简单的Java程序–手速练习
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
二、语法基础
- 变量
变量必须先定义,才可以使用。不能重名。
变量定义的方式:
public class Main {
public static void main(String[] args) {
int a = 5;
int b, c = a, d = 10 / 2;
}
}
内置数据类型:
类型 | 字节数 | 举例 |
---|---|---|
byte | 1 | 123 |
short | 2 | 12345 |
int | 4 | 123456789 |
long | 8 | 1234567891011L |
float | 4 | 1.2F |
double | 8 | 1.2,1.2D |
boolean | 1 | true,flase |
char | 2 | ‘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
是条件表达式,和whil
e中的条件表达式作用一样;可以为空,空语句表示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 - 122
,0 - 9
是 48 - 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 表示字符串
}
StringBuilder
、StringBuffer
String
不能被修改,如果打算修改字符串,可以使用StringBuilder
和StringBuffer
。
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 接口
interface
与class
类似。主要用来定义类中所需包含的函数。
接口也可以继承其他接口,一个类可以实现多个接口。
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类的继承关系
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();
}
}
完结撒花!!
感谢观看