JDK:Java 开发工具包,包含JRE、JVM,是开发环境
JRE:Java 运行时环境,包含JVM
JVM:Java 虚拟机,用来解释class文件
System.out.println(12456);
System.out.println('A');
System.out.println("A");
System.out.println("Hello Java");
System.out.println("");
//声明
int i;
//初始化
i = 520;
System.out.println(i);
//初始化
i = 1314;
System.out.println(i);
//声明
int i2;
//初始化
i2 = 666;
System.out.println(i2);
System.out.println("");
//-128~127,超出会报错
byte b = 127;
System.out.println(b);
short s = 2;
System.out.println(s);
int i3 = 3;
System.out.println(i3);
//后面数字默认是int类型,当数字超过int类型我们需要在后面加上long类型然后赋给变量。
long l = 12345678912L;
System.out.println(l);
//小数默认double,所以小数直接赋值给float会报错,需要在数字后面加F。
float f = 1.2f;
System.out.println(f);
double d = 20.1;
System.out.println(d);
/*
* 每一个字符在字符编码表中都有对应的十进制数字
*
*/
char ch = 'a';
System.out.println(ch);
char ch1 = 90;
System.out.println(ch1);
//在单引号之内,一个单位才算字符,这里有两个字符,所以报错。
char ch2 = '65';
char ch3 = '张';
System.out.println((int)ch3);
char ch4 = '无;
System.out.println((int)ch4);
char ch5 = '忌';
System.out.println((int)ch5);
boolean flag = true;
System.out.println(flag);
b = (byte)s;
b = (byte)ch;
System.out.println(b);
s = b;
s = (short)ch;
i = b;
i = s;
i = ch;
i = (int)l;
i = (int)f;
i = (int)d;
l = b;
l = s;
l = i;
l = (long)f;
l = (long)d;
l = ch;
ch = (char)l;
ch = (char)b;
ch = (char)i;
ch = (char)s;
ch = (char)f;
ch = (char)d;
i = 129;
//数据溢出
b = (byte)i;
System.out.println(b);
byte——>short——>char——>int——>long——>float——>double
注意:两个char型运算时,自动转换为int型,当char与别的类型运算时,先转换成int型,再做其它类型的自动转换
1:不能对boolean类型进行类型转化
2:不能把对象类型转换成不相关类的对象
3:在把容量大的转化成容量小的类型时必须使用强制类型转换
4:转换过程中可能导致溢出或损失精度
5:浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入
变量的重点在于 变,指在程序运行过程中值可以不断的变化
常量的重点在于 常,指在程序运行过程中不能发生改变
被final修饰的变量就叫做常量,常量只能赋值一次,一般存储不会改变的值
final int AGE;
AGE = 18;
System.out.println(AGE);
int i = 100;
System.out.println(i+50);
System.out.println(i-50);
System.out.println(i*50);
System.out.println(i/50);
System.out.println(i%50);
指的是运算之后重新赋值给变量,即使用运算符会改变变量的值
赋值运算符都会自动进行隐式强制转换
++i是指先加后用,i++是指先用后加
i+=50;
System.out.println(i);
byte b= 5;
b = (byte) (b + 5);
//隐式强制转换
b+=5;
i = 100;
System.out.println(i++);
System.out.println(++i);
字符串不能进行任何运算,只能使用+进行连接,则任何数据碰到字符串都会变成字符串
代码是从左往右,从上到下运行的。+的左右是数字就进行加法运算,如果一侧有字符串就变成字符串
System.out.println(5+5+"Hello"+5+5);
改变同一行代码中的执行顺序
System.out.println(5+5+"Hello"+(5+5));
通过代码执行,可以发现代码执行后得到的返回值都是布尔值
称所有比较运算为:布尔表达式,因为比较运算的运算结果是布尔值,通过他来给布尔变量赋值
重点:字符串以及所有的引用数据类型不能使用进行对比,因为引用数据类型使用比较的是内存地址,所以可以使用对象名.equals()方法来进行比较,equals方法可以比较引用数据类型中具体的值是否一致。
System.out.println(i == 100);
System.out.println(i > 100);
boolean boo = i < 100;
System.out.println(boo);
类型比较特殊,在创建字符串变量的时候,可以使用类似基本数据类型的方式创建
String str = "Hello";
也可以使用String构造方法来创建字符串变量
String str2 = new String("Hello");
String str3 = "Hello";
String str4 = new String("Hello");
System.out.println(str);
System.out.println(str2);
System.out.println(str == str2);
System.out.println(str == str3);
System.out.println(str == str4);
System.out.println(str.equals(str2));
System.out.println(str.equals(str3));
System.out.println(str.equals(str4));
&&短路与:当左侧条件结果为false的时候,会进行短路直接返回false,不执行右侧条件
System.out.println(!(i == 100 && (i+=200) > 100));
System.out.println(i);
&位与:必须执行完所有条件,才会返回结果,位与可以进行位运算
System.out.println(i == 100 & (i+=200) > 100);
System.out.println(i);
||短路或:当左侧条件为True的时候,会进行短路返回true
i = 101;
System.out.println(i > 100 || (i+=100)>50);
System.out.println(i);
|位或:必须执行完所有的条件才会返回结果,并且位或可以进行二进制运算
System.out.println((i+=200) > 100 | (i+=100)>50);
System.out.println(i);
可以在不同情况下来给变量副不同的值,true的时候是一种值(左边),false的时候是另一种值(右边)
int i = 59;
int number = i >100? i:100;
System.out.println(number);
这个类的对象可以扫描控制台,实现从控制台反向向程序代码中输入内容
Scanner类中所有方法都是属于对象的,所以需要先获取Scanner对象才能使用里面的方法
注意:(1)、Scanner这个类不在我们自己的包下,所以使用它时需要导包 使用java.lang包下的类,以及自己包下的类不需要导包,其余都要导包
(2)、在使用Scanner扫描流的时候,建议在输入之前给出一句提醒,必须要输入内容
Scanner sc = new Scanner(System.in);
// 从控制台输入一个字符串
String name = sc.next();
// 从控制台输入一个整数
int age = sc.nextInt();
// 从控制台输入一个小数
double d = sc.nextDouble();
System.out.println(name + " " + age + " " + d);
是Java提供给我们的数学操作类,里面有很多数字操作类方法,这里主要用它来生成随机数
Math方法中很多属于类,这些方法可以使用类名称,方法名直接调用
Math.random():会随机产生一个处于0~1之间的随机小数,最小(大)无限接近0(1)
Math.random()*10:随机数范围变成0~10 (int)(Math.random()*10):变成0~9的随机整数
(int)(Math.random()*10+1):变成1~10的随机整数
System.out.println(Math.random() * 10);
// 0~9
System.out.println((int) (Math.random() * 10));
// 0~10
System.out.println((int) (Math.random() * 10 + 1));
是Java中的选择流程控制语句,可以让我们在不同的情况下执行不同的代码
如果使用多个if,所有的if之间没有关系,可能会同时执行多个if,这时就需要将所有的if连成一个整体,这样就只执行一个if
使用else关键字,连接所有的if条件,让其变成一个整体 if else支持嵌套
System.out.println("请输入年龄:");
int age = sc.nextInt();
double price = 100;
if (age > 80) {
System.out.println("您是80以上的老人可以享受免费!!!");
} else if (age > 60) {
price *= 0.5;
System.out.println("60以上80以下老人票价为:" + price + "元");
} else if (age < 18) {
price *= 0.8;
System.out.println("未成年票价为:" + price + "元");
} else if (age >= 18) {
System.out.println("成年人全价:" + price + "元");
}
单独配合if使用时,如果if的条件为false,则执行else的代码
if语句中else单独使用是指除了if条件以外所有的情况都执行
if (age < 18) {
System.out.println("未成年");
} else {
System.out.println("成年");
}
System.out.println("请输入您的账号:");
String username1 = sc.next();
if (username1.equals("admin")) {
System.out.println("请输入您的密码:");
String password1 = sc.next();
if (password1.equals("6666")) {
System.out.println("输入正确");
} else {
System.out.println("您输入的密码有误");
}
} else {
System.out.println("您输入的账号有误");
}
Scanner sc = new Scanner(System.in);
System.out.println("请输入年份:");
int year = sc.nextInt();
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0)) {
System.out.println("闰年");
} else {
System.out.println("平年");
}
Scanner sc = new Scanner(System.in);
System.out.println("请输入年份:");
int year = sc.nextInt();
if (year % 400 == 0) {
System.out.println("闰年");
} else if (year % 4 == 0 && year % 100 != 0) {
System.out.println("闰年");
} else {
System.out.println("平年");
}
java控制语句之一,但与if不同,If可以使用范围判断
switch不支持范围判断,只支持具体的数值的判断,所以在可用性上比if差很多,一般只有选择的可能性较少的时候才使用swith
switch的语法:
switch(key){
case value:
break;
default:
break;
}
其中key需要放变量或具体的值,case后面的value不支持布尔表达式,只能放具体值
如果后面的值于key相同,则会执行case中的代码,所有的value值不能重复
如果所有的case都没匹配成功,则会执行default的代码
break:java关键字之一,作用为打断当前作用域的代码
1:switch支持byte、short、int、char、String、枚举,不支持long、float、double、boolean
2:switch括号中可以放变量以及符合要求的值,case后面只能放值
3:switch在case对比成功后,执行后面所有的代码,不再进行比较,哪怕之后case不符合要求,也会执行case里面的代码,这种特性叫switch的穿透性,所以我们需要在每个case代码的最后使用break关键字,避免执行case中代码
4:当所有case对比失败,会进入default代码,而default是可要可不要的
Scanner sc = new Scanner(System.in);
System.out.print("请输入你的数字:");
int i = sc.nextInt();
switch(i){
case 1:
System.out.println("1");
break;
case 2:
System.out.println("2");
break;
default:
System.out.println("其他");
}
Scanner sc = new Scanner(System.in);
System.out.print("请输入您的年龄:");
int age = sc.nextInt();
switch(age/20){
case 0:
System.out.println("少年");
break;
case 1:
System.out.println("青年");
break;
case 2:
System.out.println("中年");
break;
default:
System.out.println("老年");
}
System.out.println("请输入年份:");
int year = sc.nextInt();
switch(year%400){
case 0:
System.out.println("闰年");
break;
default:
switch(year%4){
case 0:
switch(year%100){
case 0:
System.out.println("平年");
break;
default:
System.out.println("闰年");
}
break;
default:
System.out.println("平年");
}
}
year = (year%100 == 0 || (year%4 ==0 && year%100 !=0))? 0:1;
switch(year){
case 0:
case 1:
System.out.println("闰年");
break;
default:
System.out.println("平年");
}
System.out.println("请玩家输入 0:石头1:剪刀2:布");
int command = sc.nextInt();
int machine = (int)(Math.random()*3);
String str= "";
switch(command){
case 0:
System.out.println("玩家出石头");
switch(machine){
case 0:
System.out.println("人机出石头");
str = "平局";
break;
case 1:
System.out.println("人机出剪刀");
str = "你赢了";
break;
case 2:
System.out.println("人机出布");
str = "你输了";
break;
}
System.out.println(str);
break;
case 1:
System.out.println("玩家出剪刀");
switch(machine){
case 0:
System.out.println("人机出石头");
str = "你输了";
break;
case 1:
System.out.println("人机出剪刀");
str = "平局";
break;
case 2:
System.out.println("人机出布");
str = "你赢了";
break;
}
System.out.println(str);
break;
case 2:
System.out.println("玩家出布");
switch(machine){
case 0:
System.out.println("人机出石头");
str = "你赢了";
break;
case 1:
System.out.println("人机出剪刀");
str = "你输了";
break;
case 2:
System.out.println("人机出布");
str = "平局";
break;
}
System.out.println(str);
break;
default:
System.out.println("玩家输入错误,游戏结束");
}
System.out.println(123); 在输出内容之后,进行换行操作
System.out.println(123); 在输出内容之后,不进行换行操作
err:红色的输出语句,err与out是两个线程,一般不建议两个一块使用,因为不同的线程执行的都是各自的顺序,数据可能会乱
System.out.printf() 在输出语句中可以使用占位符
" 输出一个双引号
’ 输出一个单引号
\n 代表换行
\t 代表一个tab的距离
\\ 输出一个
\\\\ 输出\\
System.out.println(123);
System.err.println(123);
System.err.print(123);
System.out.println(123);
System.out.printf("%d %d",12,13);
System.out.print("\n\"");
System.out.println("\'");
无论生活中,还是程序中循环都是必不可免的,在生活中戴阳每天日出日落,开车轮胎转动,打印机复印文件等;
以打印机为例:
小明复印100张身份证:1、循环内容是复印身份证 2、循环次数是100次
从上面例子中可以发现,循环需要有循环的内容,以及循环的开始是需要一定的条件的,循环一般有次数
Java中最常见的循环是for循环,for循环语法如下:
1 2 4
for(声明初始化变量;判断循环条件;改变变量的初始值){
循环的内容; 3
}
1、声明并初始化变量的操作,在循环中只执行一次
2、每次循环之前,条件为true才会执行,如果为false循环结束
3、每次循环是循环体内的代码都会执行
4、当循环结束后,会自动执行第四部分,改变变量的初始值
5、for循环中1、2、4部分都可以不写,但必须要存在两个分号,也可以只写一部分
6、声明的变量不一定非要是int,以及最后改变初始值不一定非要++,只要改变变量的值就可以了
7、for循环支持嵌套
for(int i = 0;i<10;i+=2){
System.out.println(i);
}
int m = 0;
for(;m<10;){
System.out.println(m++);
}
for(;;){
System.out.println(123);
}
break:打断当前作用域的代码,破坏掉循环结构,循环中我们可以通过标记来指定,让break打断指定作用域的代码,表示方式是使用x来标记
continue:跳出本次循环,进行下一次循环
for(int i=0;i<5;i++){
System.out.println(i+"外循环-------");
for(int j=0;j<5;j++){
if(j==2){
break;
}
System.out.println(j+"内循环------");
}
}
for(int i=0;i<5;i++){
System.out.println(i+"外循环-------");
for(int j=0;j<5;j++){
if(j==2){
continue;
}
System.out.println(j+"内循环------");
}
}
使用增强for循环遍历数组:增强for循环可以在每次循环的时候,都将数组中的值取出来赋值给前面的变量,要求前面声明的变量类型必须要与数组的类型一致
需要注意的是,数组赋值是通过下标操作,而增强for循环中数组是没有下标的,所以不能使用增强for循环给数组赋值
只能使用增强for循环读取数组的值
int[] arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*20);
}
int sum = 0;
for(int i=1;i<101;i++){
sum +=i;
System.out.print(i+(i==100? "=":"+"));
if(i<100){
System.out.print(i+"+");
}else{
System.out.print(i+"=");
}
}
System.out.println(sum);
int sum1 = 0;
for(int i = 0;i<101;i += 2){
sum1 += i;
}
System.out.println(sum1);
int sum2 = 0;
for(int i = 0;sum2 < 1000;i++){
if((i%3 ==0) || (i % 10 == 3) || (i%100== 3) || (i/10%10 == 3)){
System.out.println(i);
sum2 += i;
}
}
System.out.println(sum2);
int g,s,b;
for(int i = 100;i<1000;i++){
g = i%10;
s = i/10%10;
b = i/100;
if(g*g*g+s*s*s+b*b*b == i){
System.out.println(i);
}
}
for(int i = 1;i<10;i++){
for (int j = 1; j <= i; j++) {
System.out.print(j+"*"+i+"="+j*i+"\t");
}
System.out.println();
}
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的数字:");
int n = sc.nextInt();
if(n%2 != 0){
for (int i = 1; i <= n/2+1; i++) {
for(int j = 1;j<n/2+2-i;j++){
System.out.print(" ");
}
for(int j = 1;j<i*2;j++){
System.out.print("*");
}
System.out.println();
}
for(int i =n/2;i>0;i--){
for(int j=n/2;j>=i;j--){
System.out.print(" ");
}
for(int j = 0;j <i*2-1;j++){
System.out.print("*");
}
System.out.println();
}
}else{
System.out.println("输入有误,程序错误");
}
while循环实际上就是一个拆分的for循环,在while循环的小括号中,省略了for循环的第一步和第四步
只保留了判断循环的条件,只有当条件为true才会执行循环,如果为false则不执行
while的执行就分两步
1:判断循环条件是否成立
2:执行循环体
while循环适用于,不明确循环次数时,可以使用while循环,一般经常使用while去遍历结果集,例如通过字节读取一个文件,或者解析从数据库获取结果集
while循环与for循环一样都有可能不执行,因为他们都是先判断循环条件,再决定是否循环
int i=0;
while(i++ < 10){
System.out.println(i);
}
do{
循环代码体
}while(布尔表达式);
do while由于循环体在while上面,所有会先执行一次循环,然后去判断循环条件,如果为true则继续执行,如果为false则停止执行,由于顺序问题,代表着do while循环无论怎样都会执行一次
i=1;
do{
System.out.println("---------");
i++;
}while(i<10);
变量可以用来存储数据,但是变量有一个缺点,一个变量在同一时间只能存储一个值,如果我们代码中需要同时存储很多数据的时候,使用变量变得相当麻烦,例如:此时我们需要存储100个学生的成绩,那么我们就需要定义100个变量来实现,代码就会变得相当复杂,此时数组就可以解决这个问题。
数组:
1:如果我们将变量看成一个单独的调料瓶,那么数组就可以看成是一组调料瓶,他可以实现同时存储多个数据。
2:数组可以实现同时存储多个数据,并且通过下标来操控里面的值,需要注意的是,数组的第一次初始化的时候就已经指定
3:需要注意的是,数组的长度在第一次初始化的时候就已经指定,不能更改
元素:是指数组中保存的数据,如果没有对数组里面的元素赋值的话,里面的元素都是类型的默认值
默认值:整型默认0,浮点型0.0,布尔默认false,引用类型默认null
长度:是指数组中可以保存的元素个数,长度可以通过数组名.length属性获取长度
下标:表示的是数组中元素的位置,我们可以通过数组名[下标]来操控数组中的值,下标从0开始永远比长度-1,也就是说下标永远小于长度ArrayIndexOutOfBoundsException下标越界异常
数组的创建分两步:
1:声明 类型名 对象名[]; 例:int[] array;
2:初始化
2.1:静态初始化是指在初始化数组的时候,直接给数组里面的元素赋值
2.2:动态初始化是指在初始化数组的时候指定长度,不进行赋值
数组使用起来并不复杂,可以跟变量一样去使用,只是多了一个下标的指令
int[] array = {1,2,4,5,7};
System.out.println(array.length);
int[] array1 = new int[10];
System.out.println(array1.length);
System.out.println(array[4]);
// 由于数组可能会很长,所以我们通过一个一个下标去查看会很麻烦,所以使用 for循环遍历数组,以及对数组的赋值,总结数组的下标从0开始,永远比数组的 length小,所以可以使用for循环计数器,来充当下标来操控数组
for(int i = 0;i<array.length;i++){
System.out.println(array[i]);
}
for(int i=0;i<array1.length;i++){
array1[i] = (int)(Math.random()*10000);
System.out.println(array1[i]);
}
int[] scores = new int[5];
Scanner sc = new Scanner(System.in);
for (int i = 0; i < scores.length; i++) {
System.out.print("请输入第"+(i+1)+"名同学成绩:");
scores[i] = sc.nextInt();
}
for (int i = 0; i < scores.length; i++) {
for (int j = 0; j < scores.length-i-1; j++) {
if(scores[j]>scores[j+1]){
int temp = scores[j];
scores[j]=scores[j+1];
scores[j+1] = temp;
}
}
}
for(int i:scores){
System.out.println(i);
}
Arrays:JRE提供的一个工具类,专门用于操作数组,里面有很多关于数组的方法
Arrays.toString(array): 可以将数组转化成字符串
Arrays.sort(array); 将数组的元素进行升序排序
Arrays.binarySearch(array,13) 返回元素在数组中的下标位置,如果是负数代表不存在,二分查找,需要有序
Arrays.fill(array, 555) 将数组中的元素全部变成555
Arrays.copyOf(array, 10) 复制一个长度为10的数组,并且在有效长度之内新数组的值与老数组的值相同,超出的部分为0
Arrays.copyOfRange(array, 2, 10) 从下标2开始拷贝,到下标10截止,将内容复制到新数组中,长度不够用默认值补。
Arrays.equals(copyRange, copyRange1)对比数组中的值是否相等。
int array[] = {54,53,54,59,2,646,89,546};
//直接输出是一个内存hash地址
System.out.println(array);
System.out.println(Arrays.toString(array));
Arrays.sort(array);
System.out.println(Arrays.toString(array));
System.out.println(Arrays.binarySearch(array,13));
Arrays.fill(array, 555);
System.out.println(Arrays.toString(array));
int[] copyOf = Arrays.copyOf(array, 10);
System.out.println(Arrays.toString(copyOf));
int[] copyRange = Arrays.copyOfRange(array, 2, 10);
int[] copyRange1 = Arrays.copyOfRange(array, 2, 10);
System.out.println(Arrays.toString(copyRange));
System.out.println(Arrays.equals(copyRange, copyRange1));
System.out.println("***************************");
//字符串
String[] ss = {"b","a","c","d"};
System.out.println(ss);
System.out.println(Arrays.toString(ss));
Arrays.sort(ss);
System.out.println(Arrays.toString(ss));
System.out.println(Arrays.binarySearch(ss, "a"));
String[] s1 = Arrays.copyOf(ss, 20);
System.out.println(Arrays.toString(s1));
String[] s2 = Arrays.copyOfRange(ss, 0, 3);
System.out.println(Arrays.toString(s2));
String[] s3 = Arrays.copyOfRange(ss, 0, 3);
System.out.println(Arrays.equals(s2, s3));
冒泡排序是指,每次都是相邻的两个元素相比较,如果大于或小于对方则互换位置
int[] arr = {5,78,2,742,8};
for (int j = 0; j < arr.length-1; j++) {
//防止数组越界
for (int i = 0; i < arr.length-1-j; i++) {
//左边比右边的元素小
if(arr[i]<arr[i+1]){
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
每次循环都选取出最大或最小的下标,然后将其里面的元素放入到有序数组的最末位;简单理解,每次循环找出最大值(最小值),然后将其放入到数组的头部开始排序
for (int i = 0; i < arr.length - 1; i++) {
//刚开始i就是最大或最小值的下标
int index = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[index]) {
index = j;
}
}
//index不等于i代表i当前的元素不是最小值,所以需要换值
if(index != i){
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}
System.out.println(Arrays.toString(arr));
将排序数列分为两部分,即已经成序列和等待排序列;每次从剩余n-1个数当中,取出数与成序列从后向前进行比较,根据排序规则(若从小到大),将取出的数与较大数边比较向后移,直到找到符合位置,则继续新的一轮比较排序。
for(int i = 1;i<arr.length;i++){
//每次取出的值与前面的数比较
for (int j = i; j > 0; j--) {
if(arr[j]<arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}else{
break;
}
}
}
System.out.println(Arrays.toString(arr));
String s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String code= "";
for(int i = 0;i<6;i++){
int number = (int)(Math.random()*s.length());
code += s.charAt(number);
}
System.out.println(code);
char[] ch = new char[6];
for(int i = 0;i<ch.length;i++){
int number = (int)(Math.random()*62+48);
if(number<58){
ch[i] = (char)number;
}else if(number>57&&number <=83){
ch[i] = (char)(number+7);
}else{
ch[i] = (char)(number+13);
}
}
String s1 = new String(ch);
System.out.println(s1);
1:程序计数器:一块较小的内存,是当前线程执行的字节码的行号指示器,每个线程都有一个独立的程序计数器,并且互不影响。
2:Java虚拟机栈:线程是私有的,其生命周期与线程相同,其主要目的就是描述Java方法执行的内存模型。每个方法在执行时都会创建一个栈帧,这个栈帧存储了局部变量,操作数,操作数栈,动态链接,方法出口等一些信息,每一个方法从调用到执行完成这个过程对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表:存放着编译期可以知道的各种基本数据类型,对象的引用。
3:本地方法栈:
4:Java堆:虚拟机中最大的一块内存,堆内存被所有的线程共享,堆内存唯一的功能就是存放对象的实例,几乎所有的对象都在这里分配内存。堆内存是垃圾收集管理器管理的主要区域所以也叫GC堆。
5:方法区:与堆一样被所有线程共享,方法区主要存储的是被虚拟机加载的所有类型信息,常量,静态变量,接口,字段,方法,常量池,域信息,方法信息等内容。
常量池:字面量,符号引用
符号引用:1:类的全限定名(包名.类名) 2:字段名和属性 3:方法名和属性
类型信息:1:这个类型的全限定名 2:其直接父类的全限定名 3:类的修饰符(访问修饰符,是否抽象,是否final等) 4:这个类型直接接口的有序列表
域信息:1:成员变量的访问修饰符 2:成员变量是否静态 3:成员变量是否final 4:成员变量是否volatile(用于多线程的一个关键字) 5:成员变量是否transient(用于序列化的一个关键字) 6:成员变量的类型 7:成员变量的名称
方法信息:1、方法的访问修饰符 2、方法是否abstract或native 3、方法是否静态,是否final,是否synchronized(是否同步,多线程的一个关键字)等 4、方法的返回值类型
5、方法名称 6、方法的参数个数和类型 7、异常表
类是对象的抽象,对象是类的具体实例。解释:是一个抽象的概念,没办法指着类说它具体是谁,因为类指向的是一个大体,类里面抽离了所有实例对象共同特征形成属性和行为,在类中这些属性没有具体的值,因为这些属性虽然写在类里面,但是属性具体的值却是属于实例化出来的对象。
1:直接声明在类中,其作用域是整个类
2:全局变量有默认初始值(不需要初始化也可以直接使用)
1:被static修饰的变量,存储在常量池中,被所有对象共享,可以通过类名直接调用
2:没有static修饰的变量,存储在堆内存中,只能通过对象名调用,并且每个对象只有一份,不会共享
1:一般声明在方法中,作用域一般是声明时候的作用域,例如:如果直接声明到方法中,作用域是整个方法体,如果声明在方法的for循环中,则作用域在for循环中
2:局部变量没有默认初始值,因此在使用时必须先初始化
1:由于static修饰的变量在内存中只有一份,只要有任何对象对其进行修改就会改变值,所以一般我们会在static变量前面添加一个final关键字,已达到禁止修改
2:只有在工具类中,为了方便使用,才进行final static修饰变量,例如Math类中的常量PI
public class Cat {
int age;
static String name;
final static String sex = "女";
}
public static void main(String[] args) {
Cat c = new Cat();
//age属性则是一个对象只能改变自己的age值
c.age = 12;
//name属性是静态的,只在常量池中有且仅有一个,所以不管那个对象改变 值,都会发生改变
c.name = "加肥1号";
Cat c1 = new Cat();
c1.age = 13;
c1.name = "汤姆号";
//c1.sex = "男";会产生报错原因是sex使用final修饰,则禁止改变
System.out.println(c.age+" "+c1.name);
System.out.println(c1.age+" "+c.name);
}
1:将一些功能性代码封装起来,以便反复使用
2:所有代码都在一起比较凌乱,这时将每段代码都封装成一个方法,这样代码看起来会比较整齐
方法的语法:[ ]代表可选
[访问修饰符] [static] 返回值类型 方法名([形参列表…]){
方法体
}
主要用来控制方法可以被哪些类使用
1:public 例:公交车 代表本项目中谁都可以使用
2:protected 例:校车 代表本包下所有类,以及其他包下的子类对象可以使用
3:无 例:专线车 代表本包下所有类可以使用
4:private 例:私家车 代表只有本类中才能使用
public void eat(){
System.out.println("public");
}
protected void play(){
System.out.println("protected");
}
void swim(){
System.out.println("空");
}
private void song(){
System.out.println("private");
}
//可以调用public修饰的方法
c1.eat();
//可以调用protected修饰的方法
c1.play();
//可以调用没有修饰符修饰的方法
c1.swim();
//不可以调用private修饰的方法
c1.song();
决定好方法调用完之后,返回给调用者什么类型的值
1:写方法时,想返回给调用者什么类型的值,就写什么类型
2:如果方法有返回值,那么就在方法最后使用return关键字将数据返回给调用者
3:如果方法不用返回给调用者数据,则使用void关键字表示这个方法没有返回值
4:如果方法使用了void关键字,在方法最后就不用使用return返回值了
5:有返回值的方法,执行完成之后会变成相应的返回值,可以把它放到输出语句中,也可以使用变量接收方法的返回值
标识符之一,根据命名规则自定义命名
调用方法时需要调用者传给什么值,就声明什么
如果方法中需要一些数据,这些数据需要调用者传给我们,就可以将这些数据定义到形参列表上
方法想实现什么功能就写该功能的代码
1:被static修饰的方法,叫做静态方法,静态方法可以使用对象名调用,也可以使用类名调用,一般使用类名调用
2:被static修饰的方法,无法直接访问实例成员的属性以及实例方法
3:不被static修饰的方法,叫做实例方法,实例方法只可以使用对象名调用
4:实例方法可以访问所有的变量以及方法
构造方法是为了专门创建对象而存在的
1: 构造方法没有返回值,因为它就是为了创建对象而存在的
2:构造方法必须创建自身类的对象,所以方法名必须与自身类一致
3:如果类中不写构造方法,则默认自动生成无参构造
4:构造方法可以重载
当前使用的对象,代码执行到哪个对象,this就代表哪个对象
1:使用this.属性是调用当前对象的属性
2:使用this.方法是调用当前对象的方法
3:this()是调用自身构造方法,注意:this()只能在构造方法中使用
当方法中局部变量和全局变量冲突时,要想使用全局变量就需要使用this关键字
1:将一组对象的共有属性与行为抽象成一个类
2:将属性私有化,提供公开的方法来访问属性,方法的要求如下:
2.1: 赋值的方法名要set开头,后面跟上属性名,例setName
2.2:取值的方法要get开头,后面跟上属性名,例getName。由于获取值需要有返回值,所以取值的方法需要有返回类型,并且一定是无参的
3:将一组功能性代码,封装成方法以便反复使用
子类可以通过使用extends关键字去指定自己的父类,然后继承父类非私有属性及方法
1:只支持单继承,也就是说类只能继承一个类
2:每个类可以被继承多次
3:类中每个类都有自己的父类,如果没有指定父类,那么默认继承Object类,所以Object类是所有类的超类(基类)
4:子类可以从父类继承方法,如果子类对继承的方法不满意可以自己重写这个方法,如果子类重写了这个方法,那么调用时会优先调用重写的这个方法
5:构造方法不能被继承
6:在创建子类对象时,JVM会先调用父类无参构造方法去创建父类的实例化对象,然后再去创建子类的实例化对象
7:如果父类没有无参构造方法,我们需要在子类的构造方法中使用super关键字来显示父类构造方法
super()表示调用了父类构造方法
super.属性表示调用了父类的属性
super.方法()表示调用了父类的方法
1:是发生在父子类之间的,方法名需要一致
2:返回类型需要一致
3:形参列表需要一致
4:访问修饰符的权限需要比父类的高
当我们将对象通过输出语句输出的时候,对象会默认调用toString方法,我们只需要重写toString方法就可以在输出语句输出对象的时候,返回对象信息的字符串
1:修饰在变量上,让其变成一个常量
2:修饰在类上,不能被继承,例String就是final修饰的类,不能被继承
3:修饰在方法上,该方法不能被子类重写
1:修饰在变量上,变量变成静态成员,可以被类直接调用
2:修饰在方法上,方法变成静态方法,可以被类直接调用
3:修饰在类上,变成静态类
4:修饰在代码块上,叫做静态块
1:静态块 直接写在类中,只有类在第一次被加载时才执行一次
2:构造块 直接写在类中,每次创建对象时,都会调用一次
3:普通块 写在方法之后,可以避免方法中变量的冲突,同时局限了变量的作用域
4:同步块 多线程时候再解释
静态块——优先所有——>构造块——优先自身——>构造方法
1:被abstract class修饰
2:可以有普通类的一切(实例变量、静态变量、实例方法、静态方法、构造器、代码块等),以及抽象方法
3:有构造器,但不能实例化对象
4:可以继承于其它类,但不能被final修饰,因为抽象类中可能存在抽象方法
1:被abstract修饰
2:并且没有方法体
3:子类必须实现父类的抽象方法,除非子类也是抽象的
4:抽象方法只能定义在抽象类或接口中
5:抽象方法不能是private的,并且不能是static的,final的,synchronize的,native的
1:实际开发中很少使用继承,因为Java中的继承是单根的,如果随便继承一个父类,代码开发中如果需要继承其他的类,此时只能推翻重写,所以尽量从一开始就避免使用继承,只有在迫不得已的情况下才使用继承
2:如果真的需要使用继承的话,父类尽量定义成抽象类
1:有时候父类的功能真的很强大,我们需要继承里面的一些功能。例:HttpServlet
2:为了统一规范(Animal抽象类,所有的狗、猴子、老虎、狐狸都可以去继承他们共有的行为eat)
interface:用来标识接口
class:用来标识类
1:接口中只能有常量,并且接口中的常量默认必须被public static final修饰,而抽象类中可以有成员变量
2:接口中的实例方法必须被default修饰,并且访问修饰符默认必须是public,而抽象类实例方法随意
3:接口中静态方法默认必须是public修饰符,而抽象类随意
4:接口中没有构造器,而抽象类中有
5:接口中没有静态块、构造块,而抽象类中全有
6:接口中抽象方法默认被public abstract修饰,而抽象类需要手写public abstract
7:接口是被类用来实现的(普通类实现接口,必须添加所有的抽象方法实现),而抽象类是用来被继承的
8:接口可以被多实现,而抽象类只能单继承
9:接口可以多继承接口,而抽象类只能单继承
1:接口对比起抽象类来讲更加抽象(接口可以理解成抽象类的升级抽象版)
2:一个类可以实现多个接口,并不影响继承
3:如果能使用接口,就尽量使用接口不要使用继承(尽量使用接口代替抽象类)
1:主要用来充当方法的目录,以及统一所有实现类中方法的规范
2:在以后的项目中,我们可能会使用很多常量值,这时就可以把这些常量定义到接口中,方便使用
重点:虽然JDK1.8之后接口中的方法可以有方法体了,但是为了保持接口的抽象我们一般不准在接口内写实例方法。
就是指多个形态,所谓的多态就是指引用数据类型的向上转换,将子类的实例化向上转成了父类的类型,既然存在向上转型,就同时存在向下转型,向下转型是指将父类的类型强转成子类的类型
1:多态是指声明的是父类(包括接口)的类型,但是实例化的却是子类(实现类)的对象
2:继承或实现+方法的重写是多态的前提条件
1:可替换性
2:可扩充性(接口或父类新增实现类或子类不影响之前的代码结构)
3:灵活性
4:简化性
1:当我们使用多态时,只能调用子类重写父类的方法,无法调用自身独有的方法
2:如果需要调用子类自己的方法,需要进行向下转型的操作
可以判断对象是否可以转换成对应的类型,如果可以返回true,不可以返回false
应用程序接口,API实际上就是一个应用程序的说明书,用于查找工具类的方法
所有异常以及错误体系的顶级父类,其下面有两个子类分别是Error、Exception
表示的是错误,一般都是指硬件出现问题,或者程序结构出现问题,这种错误程序员无法通过代码捕获,而且一般情况下无法通过代码解决。例如:JVM内存不足、程序运行中断电、JVM内部出现问题等物理问题
表示异常,一个合理的应用程序应该去捕获并且处理异常,异常分为两种编译期异常、运行期异常
在代码编译的过程中,程序不会报错,但是在运行的过程中可能会出现异常情况
1、数组越界异常:java.lang.ArrayIndexOutOfBoundsException
2、算数异常:java.lang.ArithmeticException
3、空指针异常:java.lang.NullPointerException
特点:当别人调用方法的时候,不会强制让调用者处理异常
编译器异常是指在程序编译的阶段就必须处理的异常,如果不处理的话就会出现编译错误
特点:当别人调用方法的时候,需要强制让调用者处理异常
try{
写有可能出现异常的代码
}catch(捕捉异常类型 对象名){
处理异常的代码
}
public static void b(){
try{
Class.forName("com.acv");
}catch(ClassNotFoundException e){
e.printStackTrace();
}finally{
System.out.println("抛出异常");
}
}
使用throws关键字抛异常
public static void main(String[] args) throws ClassNotFoundException{
a();
}
public static void a() throws ClassNotFoundException{
Class.forName("com.zhiyou");
}
在方体中抛出异常
public static void main(String[] args){
b();
}
public static void b(){
try{
Class.forName("com.acv");
}catch(ClassNotFoundException e){
e.printStackTrace();
}finally{
System.out.println("抛出异常");
}
}
当程序出现异常之后,如果程序员没有捕获异常,那么异常会由JVM虚拟机自动处理,JVM处理方式:
1、打印异常信息
2、关闭虚拟机。这样会造成程序运行中断,而如果程序员捕获了对应的异常信息,那么就会由程序员写的catch代码块来处理异常,JVM不再插手
1、使用System.exit(0);关闭虚拟机
2、还没执行到finally的时候,因为意外或各种情况造成虚拟机关闭或系统错误
3、在执行try之前就碰到return。则不会执行finally
return分为两步执行,1、先将需要返回的数据存入到内存之中 2、执行finally的代码3、继续执行return将内存中的值返回给调用者
异常实际上是一个类似警告牌一样的功能,在方法中合理创建异常对象可以提醒调用者需要注意的内容,以及在方法调用者出现问题的时候可以给出解决提示
1、当程序运行过程中出现异常之后,JVM会检测当前出现异常的代码是否存在于try作用域之内
2、代码不在try作用域,JVM会自己捕获异常然后进行处理
3、JVM处理异常的方式先打印异常的相关信息,然后关闭虚拟机
4、如果代码存在于try作用域,JVM会将异常对象的类型按照catch的书写顺序与所有的catch一个一个进行比对,如果所有的catch全都匹配不成功,JVM会自己处理异常
5、如果catch匹配成功,JVM会将异常对象注入到catch域的形参之中然后执行catch作用域代码
6、当catch作用域代码执行完成后,继续执行finally里面的代码,然后继续往下执行下面的代码
RuntimeException:是所有运行期异常的父类,所有的运行期异常全部都继承于RuntimeException,如果我们需要自定义异常,只需要继承RuntimeException即可
public class MyRuntimeException extends RuntimeException{
/**
* 通过有参构造,传递异常的具体信息
*/
public MyRuntimeException(String s){
super(s);
}
public static void main(String[] args) {
try {
method(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void method(int i) throws MyRuntimeException{
if(i>100){
throw new MyRuntimeException("传入的值不能大于100");
}
System.out.println("代码正常执行");
System.out.println(i+100);
}
Exception:所有异常的父类,其和所有除了RuntimeException的子类都是编译期异常,如果我们需要自定义编译期异常只需要继承Exception即可
public class MyException extends Exception{
public MyException(String s){
super(s);
}
}
public static void main(String[] args) {
try {
method(11);
} catch (MyException e) {
e.printStackTrace();
}
}
public static void method(int i) throws MyException{
if(i > 100){
throw new MyException("输入的值不能大于100,当前值为:"+i);
}
System.out.println("正常运行");
System.out.println(i+1);
}
throw:用于在方法之内,抛出异常对象给虚拟机,如果想在写方法的时候抛出异常对象必须使用throw才可以,简单点说,throw就是用来创建异常对象的
throws:用于方法体之上,将方法可能出现的异常交由方法的调用者进行处理,这个关键字自身不会产生异常对象,简单点说,throws是一种消极的解决异常的方式
当我们在写一些方法提供别人使用的时候,可以用throw去创建一些编译器异常,用于提醒方法调用者一些信息,此时我们需要使用throws将异常交给方法调用者处理,才能让对方注意到
将类中属性类型的确定工作推迟到实例化对象的时候来决定
public static void main(String[] args) {
A<Dog> a = new A<Dog>();
a.t = new Dog();
System.out.println(a);
A<Integer> c = new A<>();
c.t = 123;
System.out.println(c);
}
class A<T>{
T t;
@Override
public String toString() {
return "A [t=" + t + "]";
}
}
class Dog{
private int id;
private String name;
public int getId(){
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Dog [id=" + id + ", name=" + name + "]";
}
自动装箱:基本数据类型转换成包装类对象
Integer i1 = new Integer(15);
Integer i2 = new Integer(15);
//18是基本数据类型,i3、i4是包装类,所以进行了自动装箱
Integer i3 = 18;
Integer i4 = 18;
自动拆箱:包装类的对象变成基本数据类型 例如,当我们使用包装类对象与基本数据类型相比较时,JVM会将包装类对象自动拆箱为基本数据类型再进行比较
System.out.println((i1 == 15)+".....");//true
1:将基本数据类型存入到集合中
2:基本数据类型与包装类对象进行比较
3:包装类对象进行运算的时候
4:三目运算符中使用
5:方法形参、方法返回值
手动装箱
Integer i = Integer.valueOf("132");//手动装箱
手动拆箱,手动拆装箱主要用于不同数据类型之间的转换
Short j = i.shortValue();//手动拆箱
string:相同情况下执行时间最长,是不可变字符串,不断在常量池里面创建新的字符串,运行多少次,就创建多少个对象
StringBuffer:相同情况下执行第二少,可变字符串,无论改变多少次,还是一个对象,不会创建多个对象,耗费的时间主要是值的改变上
StringBuilder:功能与StringBuffer一致,API也一致,区别在StringBuilder线程是不安全的,StringBuffer线程是安全的,StringBuffer速度略慢与StringBuilder
当字符串的值不需要改变时,用String;当值要经常改变时,要使用StringBuilder或StringBuffer
String s = "张三";
StringBuilder sb1 = new StringBuilder();
StringBuffer sb = new StringBuffer();
long l = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
sb.append(i);
s += i;
sb1.append(i);
}
System.out.println("间隔时间:"+(System.currentTimeMillis()-l));
三大类:list、set以及map,其中list与set都是单列集合,map是key、value键值对
名称 | 类型 | 作用 |
---|---|---|
Iterable | 接口 | 里面封装一个迭代器对象,以及一个forEach迭代方法 |
Iterator | 接口 | 迭代器的顶层接口,定义了迭代器的规范 |
Collection | 接口 | 所有单列集合的顶级接口,里面定义了所有单列集合共有的功能,并且继承了Iterable的迭代器 |
List | 接口 | 有序可重复单列集合的顶级接口,里面定义了所有有序单列集合的共有功能,并且继承了Collection |
Set | 接口 | 无序不重复单列集合的顶级接口,继承了Collection |
Queue | 接口 | 队列的顶级接口,继承于Collection |
AbstractCollection | 抽象类 | 对Collection接口提供了基本的实现 |
AbstractList | 抽象类 | 对List接口的内容提供了基本的实现,同时继承了AbstractCollection |
ArrayList | 集合类 | 继承于AbstractList,并且实现了List接口 特点:1、底层完全由数组实现,有序并且可重复 2、默认初始容量为10,每次扩容1.5倍,可以通过构造方法指定初始容量 3、由于存在下标,所以查询和修改速度加快,但是删除和插入较慢 4、线程不安全,效率较快 |
Vector | 集合类 | 继承于AbstractList,并且实现了List接口 特点:1、底层完全由数组实现,有序并且可重复 2、默认初始容量为10,每次扩容1.5倍,可以通过构造方法指定初始容量 3、由于存在下标,所以查询和修改速度加快,但是删除和插入较慢 4、线程安全,效率较慢 |
AbstractSequentialList | 抽象类 | 继承于AbstractList,定义了一些为链表提供的顺序操作 |
Stack | Vector的子类,诞生于jdk1.0,基本没有使用 | |
Deque | 接口 | 是一个双端队列,继承于Queue接口 |
ArrayDeque | Deque的实现类,是一个双端队列的集合,同时继承了AbstractCollection | |
LinkedList | 集合类 | 实现了Deque队列接口以及List接口的同时,又继承了AbstractSequentialList 特点:1、底层是由双线链表+双端队列实现 2、由于链表的底层不存在原生下标,所有查询和修改较慢(没有下标,需要不断遍历),插入和删除较快(没有下标,不需要维护) |
AbstractSet | 抽象类 | 对Set接口的内容提供了基本的实现,同时继承了AbstractCollection以及实现了Set接口 |
HashSet | 集合类 | 实现了Set接口,同时继承了AbstractSet 特点:1、无序的,并且值不能重复 2、在代码中创建了一个HashMap对象,将值存入到Map的Key上,所有操作都是通过HashMap完成 |
SortedSet | 接口 | 里面定义了一些比较方面相关的内容,想为Set提供排序的方法 |
NavigableSet | 接口 | 完善了SortedSet比较和排序的方法规范,继承了SortedSet |
TreeSet | 集合类 | 实现了NavigableSet接口,同时继承了AbstractSet 特点:1、可以按照书写规则对元素进行排序,值不能重复 |
Map | 接口 | 是一种键值对集合,里面的数据是key value形式存放,在接口里定义了一些对键值对的通用操作 |
AbstractMap | 抽象类 | 实现了Map中的大部分的方法,并在里面封装了Collection对象以及Set对象 |
ConcurrentMap | 接口 | 为了创建一个线程安全 |
Dictionary | 抽象类 | 在JDK1.0就有的键值对,也是最老的键值对抽象类 |
Hashtable | 诞生于JDK1.0,继承了Dictionary的同时又实现了Map接口,目前Hashtable已经弃用 | |
HashMap | 集合类 | 继承于AbstractMap,实现了Map接口,线程不安全(最常用的Map集合) |
TreeMap | 集合类 | 继承于AbstractMap,实现了NavigableSet接口(功能上可以参考TreeSet,基本不使用) |
WeakHashMap | 集合类 | 继承于AbstractMap,实现了Map接口,里面存放的key都是弱引用对象(基本不用) |
ConcurrentHshMap | 集合类 | 继承于AbstractMap,实现了ConcurrentMap接口,主要目的就是为了解决HashMap中线程不安全的问题 |
1:先通过HashCode求出一个数值,如果这个数值跟已有的冲突的话,会调用key的equals方法来判断是否真的出现hash冲突,还是重复插入
2:如果出现hash冲突则会使用链表形式存储出现的hash冲突的元素
1:由于这个数字是根据属性的值来生成,所以如果对象的值一样,生成的数字肯定一致
2:由于hash冲突存在,有可能虽然属性的值不同,但是有可能生成了一样的数字
@Override
public int hashCode() {
// 声明一个局部常量31
final int prime = 31;
// 定义记录总数的值
int result = 1;
//根据age属性的值,求出了一个新值
result = prime * result + age;
result = prime * result + ((name == null ? 0:name.hashCode()));
//根据name属性更新了值
return result;
}
@Override
public boolean equals(Object obj) {
// 自己与自己进行对比,那值肯定一致,所以直接返回true
if (this == obj) {
return true;
}
// 空代表没有东西,直接false没必要比较
if (obj == null) {
return false;
}
// 排除obj是其他类对象的可能性
if (getClass() != obj.getClass()) {
return false;
}
// 在这里确定了obj是当前类的对象了,就可以对其进行向下转型
Dog d = (Dog) obj;
// 双方age不相等
if (age != d.age) {
return false;
}
// 自身的name是空
if (name == null) {
// 两者name不相等
if (d.name != null) {
return false;
}
} else if (!name.equals(d.name)) {
return false;
}
return true;
}
需要实现Comparable接口,然后在里面通过compareTo方法来完成排序,排序的规则为:返回正数排在后面,返回负数排在前面,返回0的话TreeSet会认为是相同的对象,造成添加数据失败
@Override
public int compareTo(Dog d) {
if(this.age>d.age){
return 1;
}else if(this.age < d.age){
return -1;
}else{
if(this.name.hashCode()>d.name.hashCode()){
return 1;
}else if(this.name.hashCode() < d.name.hashCode()){
return -1;
}
}
return 0;
}
1:Hashtable诞生于JDK1.0,而HashMap诞生于JDK1.2,并且两者的编写者不同
2:Hashtable继承于Dictionary,而HashMap继承于AbstractMap
3:Hashtable与HashMap因为父类不同的原因,导致提供的API不完全一样(Hashtable多了elements,contains方法)
4:Hashtable初始容量是11,每次扩容是2N+1,HashMap初始容量是16,每次扩容是2N
5:Hashtable调用的是key自身的hashCode方法,来求出hash值,而HashMap使用的是自己的hash方法来求出hash值
6:Hashtable线程安全,效率极低,而HashMap线程不安全。效率较高
7:Hashtable的key value都不支持存储null,HashMap的key value都支持存储null
HashMap的底层是由数组+链表组成,在JDK1.8的时候为了避免链表过长导致效率低下,引入了红黑树
使用values方法
//根据值value遍历
Collection<Integer> map1 = map.values();
for (Integer i : map1) {
System.out.println(i);
}
使用keySet方法
//根据键key遍历
Set<String> set = map.keySet();
for(String s:set){
System.out.println(s);
}
使用entrySet方法
//根据键key值value遍历
Set<Entry<String, Integer>> entry = map.entrySet();
for(Entry<String ,Integer> e:entry){
System.out.println(e.getKey()+"--------"+e.getValue());
}
文件操作类,每一个File的实例对象都代表着一个文件或文件夹
File的一些方法
File file = new File("D:\\z\\hello.txt");
if(!file.getParentFile().exists()){//判断是否存在文件夹
file.getParentFile().mkdirs();//创建文件夹
file.createNewFile();//创建文件
}
//判断文件是否存在
System.out.println(file.exists());
//判断是否是文件,如果是文件返回true,如果是文件夹返回false
System.out.println(file.isFile());
//判断是否是文件夹
System.out.println(file.isDirectory());
//获取文件的大小(字节)
System.out.println(file.length());
//获取文件的路径以及名称
System.out.println(file.getPath());
//获取文件的名字
System.out.println(file.getName());
//获取文件最后修改日期
System.out.println(new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(file.lastModified()));
//删除文件
file.delete();
//删除文件夹
file.getParentFile().delete();
File file = new File("C:\\");
if(file.isDirectory()){//判断是否是文件夹
File[] files = file.listFiles();//获取文件夹下的所有内容
if(files != null){//判断获得的文件不为空
for(File f : files){
method3(f);
}
}
}else{
System.out.println("第"+(++i)+"文件名:"+file.getPath());
}
InputStream in = new FileInputStream(file);
byte[] b = new byte[1024];
int i = 0;
//read方法会返回int值,这个返回值代表是否还存在数据,如果不存在为-1
while(!((i = in.read(b))==-1)){
System.out.println(new String(b,0,i));
}
in.close();
File file = new File("D:\\hello.txt");
if(!file.exists()){//判断是否存在该文件
file.createNewFile();//创建文件
}
//打开输出流,默认每次写的内容覆盖之前的内容
OutputStream out = new FileOutputStream(file,true);
//准备需要输出的文本
String s = "dklhsajkldhksajdhfkjsa\r\n";
//将文本转换成字节数组,然后通过输出流写入到缓存中
out.write(s.getBytes());
//将缓存的内容写入文件
out.flush();
//关闭输出流
out.close();
File file = new File("D:\\hello.txt");
Writer w = new FileWriter(file,true);
w.write("dsjkagdkjsagdkjgaskjdgjksa\r\n");
w.flush();
w.close();
Reader r = new FileReader(file);
char[] ch = new char[1024];
int i = 0;
while(!((i = r.read(ch)) == -1)){
System.out.println(new String(ch,0,i));
}
序列化必须用到Serializable空接口,来给`类进行标记,这个标记是给JVM虚拟机看的,当JVM虚拟机发现类实现了这个接口之后,就允许这个类的对象进行序列化操作
Dog1 dog = new Dog1(123,"小天",2000,"田园犬");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("D:\\dog.hhh")));
out.writeObject(dog);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\dog.hhh"));
Dog1 dog1 = (Dog1)in.readObject();
System.out.println(dog1);
in.close();
可以在序列化的时候用来指定那些属性不需要序列化(序列化对象时避开这个属性)
public static void main(String[] args) throws IOException {
File file = new File("C:\\Program Files (x86)\\Nwt");
long l = System.currentTimeMillis();
copyMethod(file);
System.out.println(System.currentTimeMillis()-l);
}
private static void copyMethod(File file) throws IOException {
if(file.isDirectory()){//判断是否是文件夹
File[] files = file.listFiles();//接收文件夹里面的所有文件
if(files != null){//文件不为空
for (File file2 : files) {//循环文件夹里面的所有文件
//递归重新进行判断文件夹里面的文件是否还有文件夹
copyMethod(file2);
}
}else{
file.getParentFile().delete();//删除空的文件夹
}
}else{
String path = file.getPath();//获取文件的地址以及名称
String newPath = path.replace("C", "D");//使用String类中replace方法替换原路径的指定字符串,以达到移动盘符的目的
File f = new File(newPath);//声明一个新的文件
if(!f.exists()){//判断文件是否为空
f.getParentFile().mkdirs();//创建多个文件夹
}
InputStream in = new FileInputStream(path);//声明输入流对象
//声明输出流对象
OutputStream out = new FileOutputStream(newPath);
//字节数组,用来存储文件里面的字节
byte[] b = new byte[(int)file.length()];
in.read(b);//读取文件里面的字节存入字节数组
out.write(b);//把读取的文件内容写入缓存
out.flush();//把缓存中的内容存入文件
in.close();//关闭输入流
out.close();//关闭输出流
file.delete();//删除空的文件
}
}
进程一般是指应用软件打开之后形成的,而每个进程都是由一个或多个线程来组成的。
多线程可以让一些程序并行运行,提高效率
1:继承Thread类,Thread类是java中的线程类,类中封装了很多与线程有关的操作,包括线程的启动,线程的命名,线程的优先级等
MyThreadExcise my = new MyThreadExcise();
Thread t1 = new Thread(my,"售票员A");
t1.setPriority(1);
t1.start();
Thread t2 = new Thread(my,"售票员B");
t2.setPriority(10);
t2.start();
Thread t3 = new Thread(my,"售票员C");
t3.setPriority(2);
t3.start();
2:实现Runnable接口,最常用的
MyRunnableExcise my = new MyRunnableExcise();
Thread t1 = new Thread(my,"售票员A");
t1.setPriority(5);
t1.start();
Thread t2 = new Thread(my,"售票员B");
t2.setPriority(8);
t2.start();
Thread t3 = new Thread(my,"售票员C");
t3.setPriority(10);
t3.start();
3:实现Callable接口,了解
MyCallableExcise my = new MyCallableExcise();
FutureTask<String> f1 = new FutureTask<>(my);
FutureTask<String> f2 = new FutureTask<>(my);
FutureTask<String> f3 = new FutureTask<>(my);
new Thread(f1,"售票员A").start();
new Thread(f2,"售票员B").start();
new Thread(f3,"售票员C").start();
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
4:通过线程池实现多线程
方法源自Thread类,作用是用于线程休眠,参数为毫秒,当线程执行到这个方法的时候会进行休眠操作,时间到了之后,会自动启动线程
1:wait属于Object类,sleep属于Thread类
2:wait方法会释放掉锁,sleep不会释放锁
3:wait方法可以不指定时间,等待notify或notifyAll唤醒,sleep必须指定睡眠时间
notify是谁先睡眠先唤醒谁,notifyAll是直接唤醒所有睡眠的线程
在多线程的情况下如果碰到网络阻塞等情况可能会造成数据错乱等情况
java中的悲观锁,修饰在方法之上的时候,这个方法只允许一个线程进入并且执行
将需要同步的代码,尽量减少,让其他的代码能被多线程执行
使用同步代码块锁住HashMap对象
同步代码块越少,执行的效率越高,所以我们在使用同步锁的时候,需要考虑的唯一问题就是如何减少同步代码块中的代码
1:新建(此时线程未启动,指的是刚new出现线程对象)
2:就绪(此时线程已启动,指的是我们刚调用start方法,此时线程进入到就绪队列等待CPU分配资源)
3:运行(线程抢到CPU资源开始执行run方法任务,此时除非线程自动放弃CPU资源否则会一直执行到任务结束)
4:阻塞(表示线程已经抢到了CPU资源,但是因为意外情况暂停了,例如sleep、wait等,此时线程可能会让出资源进入到休眠状态)
5:死亡(当线程任务执行完成,或者被杀死之后都会进入到死亡状态,而死亡状态的线程无法进入到就绪状态)
一般是由严重的逻辑引出,一般我们能通过代码来避免。死锁就是指线程A在等待线程B,而线程B也在等待线程A,双方一直在等待
被public修饰的类,要求类名称与文件名完全一致
没有被public修饰的类,对类名称没有要求,只要同包下不冲突即可
写在别的类中的类(嵌套类),外部类无法访问内部类的一切,因为初始化外部类对象的时候JVM不会自动加载内部类
实际上在正常开发中,内部类这种东西可有可无, 一般情况下我们基本可以无视,但是如果去阅读一些源码或者以后进行一些深层次的
1:可以拥有实例成员以及静态成员
2:无法直接使用外部类的实例成员
1:只能拥有实例成员
2:可以直接使用外部类的一切
public static void main(String[] args) {
//内部类创建对象
A.Inner i = new A().new Inner();
i.method();
//静态内部类创建对象
A.Inner1 i1 = new A.Inner1();
i1.method();
A a = new A();
a.method();
a.method1();
}
class A{
private int i = 10;
private static int k = 15;
public class Inner{
int i = 10000;
public void method(){
//调用静态方法
method2();
method();
System.out.println(i);
System.out.println("静态变量"+k);
}
}
//静态内部类只能访问外部类的静态成员
public static class Inner1{
int i = 5;
public void method(){
//调用外部类静态的属性及方法
method2();
System.out.println("静态变量"+k);
}
}
public void method(){
//int i;
//局部内部类
class Inner2{
public void method(){
//调用外部类静态方法
method2();
//调用外部类属性
System.out.println(i);
//调用外部类静态属性
System.out.println("静态变量"+k);
}
}
//局部内部类只能在局部范围创建对象并使用
Inner2 i2 = new Inner2();
//调用外部类的方法
i2.method();
}
//外部类静态方法
public static void method2(){
System.out.println("外部类的静态方法");
}
//外部类普通方法
public void method1(){
//创建内部类对象
Inner i = new Inner();
//使用内部类的属性
System.out.println(i.i);
System.out.println("普通方法");
//创建静态内部类对象
Inner1 i1 = new Inner1();
//使用静态内部类的属性
System.out.println(i1.i);
}
}
没名字的类,一般在new接口或抽象类的时候出现,用于给抽象方法提供默认实现
public static void main(String[] args) {
//匿名内部类
B bb= new B() {
@Override
public void method() {
// TODO Auto-generated method stub
System.out.println("aaaaaaa");
}
};
bb.method();
}
interface B{
void method();
}
1:局部内部类一般是指声明在方法体之内的类
2:可以访问并更改全局变量,但是需要受到方法体的影响(static)
3:可以访问局部变量,但是无法改变方法体之内的局部变量的值
框架的灵魂,实际上不会反射并不影响我们去正常开发,但是如果我们会用反射,并且对反射有一定理解的话,我们就能自己去研发一些简易的框架,或者去阅读框架的源码,Java中稍微高级点的内容几乎都与反射有密不可分的关系
反射是指,将代码中的一切都当成对象,通过获取类的.class文件对象,来调用属性、方法、以及构造器等
1:对象名.getClass方法
2:通过类名.class属性
3:通过Class.forName(“类的全限定名”)
Java中一直有一句话,叫做万物皆对象,这样的话我们来理解一下下面的这段话
1:编译出来的.class文件也是一个对象(Class类)
Class<?> class1 = Class.forName("com.ishangu.Dog");
2:写在类中的属性也是一个一个对象(Field类)
Dog d = new Dog(5);
//Class class2 = new Dog(5).getClass();
//Class c = Dog.class;
//这里的class1是Dog.class文件的对象,他的类属于Class
Class class1 = Class.forName("com.ishangu.Dog");
//通过.class文件获取有权限使用的属性
Field field = class1.getField("color");//根据名称直接获取单个属性
//获取属性的名字以及属性的类型
System.out.println(field.getName()+" "+field.getType());
//获取所有有权限使用的属性
System.out.println("有权限使用的属性有下面这些:");
Field[] fields = class1.getFields();
for (Field field2 : fields) {
System.out.println(field2.getName()+field2.getType());
}
//获取类中声明的所有属性(无论是什么访问修饰符)
System.out.println("class中所有的属性有下面这些:");
Field[] declaredFields = class1.getDeclaredFields();
for (Field field2 : declaredFields) {
System.out.println(field2.getName()+" "+field2.getType());
}
Field id = class1.getDeclaredField("id");
Field name = class1.getDeclaredField("name");
Field color = class1.getDeclaredField("color");
//使用反射破坏属性的访问修饰符,然后给对象的属性赋值
id.setAccessible(true);
id.set(d, 1);
name.setAccessible(true);
name.set(d, "小黑");
color.set(d, "蓝色");
//获取对象的属性值
System.out.println(id.get(d));
System.out.println(name.get(d));
System.out.println(color.get(d));
System.out.println(d);
3:写在类中的构造器也是一个对象(Constructor类)
Class<?> class1 = Class.forName("com.ishangu.Dog");
//通过class1获得所有有权限的构造方法
Constructor<?>[] c1 = class1.getConstructors();
for (Constructor<?> constructor : c1) {
System.out.println(constructor.getName()+"参数有:"+constructor.getParameterCount());
Parameter[] p1 = constructor.getParameters();
for (Parameter parameter : p1) {
System.out.println("构造器参数的类型:"+parameter.getType());
}
}
//通过class1获取本类中所有构造方法
Constructor<?>[] c2 = class1.getDeclaredConstructors();
for (Constructor<?> constructor : c2) {
System.out.println(constructor.getName()+"参数有:"+constructor.getParameterCount());
Parameter[] p2 = constructor.getParameters();
for (Parameter parameter : p2) {
System.out.println("构造器参数类型:"+parameter.getType());
}
}
System.out.println("*****************************************");
//创建无参构造器对象
Constructor<?>c3 = class1.getDeclaredConstructor();
c3.setAccessible(true);
Dog d = (Dog)c3.newInstance();
System.out.println(d);
//创建有参构造器对象
Constructor<?>c4 = class1.getDeclaredConstructor(int.class);
Dog d1 = (Dog)c4.newInstance(123);
System.out.println(d1);
//修改有参构造器的属性
Field id = class1.getDeclaredField("id");
id.setAccessible(true);
id.set(d1, 123);
Field name = class1.getDeclaredField("name");
name.setAccessible(true);
name.set(d1, "小黑");
Field color = class1.getDeclaredField("color");
color.set(d1, "黑色");
System.out.println(d1);
4:写在类中的方法也是一个对象(Method类)
Dog d = new Dog(1);
Class<?> class1 = Class.forName("com.ishangu.Dog");
//通过.class文件获取有权限使用的方法(包括父类的方法)
Method[] methods = class1.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println("************************");
//获取类中声明的所有方法(本类中声明的,所有访问修饰符都能获得)
Method[] method2 = class1.getDeclaredMethods();
for (Method method : method2) {
System.out.println(method.getName()+"参数个数:"+method.getParameterCount()+"返回值类型:"+method.getReturnType());
//获得方法的形参列表
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter.getName()+"类型是:"+parameter.getType().getName());
}
}
System.out.println("**************************");
//单独根据名称获得某个方法
Method m = class1.getDeclaredMethod("method2", String.class);
m.setAccessible(true);
//通过方法对象,invoke传入调用方法的对象,以及参数即可调用方法
m.invoke(d, "哈士奇");
5:方法中的形参也都是对象(Parameter类)
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter.getName()+"类型是:"+parameter.getType().getName());
}
所谓设计模式,是指被大多数人知晓,分类,并且反复使用的代码总结。java中的设计模式主要有23种
主要目的是为了提升代码的可重用性以及可靠性,并且让代码更好被人理解,设计模式可以让我们的代码更加规范以及工程化
那个原则:开闭原则,对扩展开放,对修改关闭(是指如果需要新增功能,可以去扩展一些类,而不修改原有代码)
1:单一原则
2:里式替换原则
3:依赖倒转原则
4:接口隔离原则
5:迪米特法则(最少知道原则)
6:合成复用原则
在程序中,我们经常到处new对象,每new一个对象都会在堆内存开辟一块空间,那如果对象非常多的情况下,内存可能会溢出
所谓单例模式就是指,在程序运行过程中无论获取多少次类的实例,获得的永远都是一个对象,不会造成对象过多的情况,减少系统开销,减轻GC的压力
java的引用数据类型之一,枚举实际上就是一个特殊点的类,这个类的构造方法必须是私有化的。