Java小记(完整版)觉得有用的话去GitHub点个小星星❤️❤️❤️❤️❤️❤️❤️ JAVASE Java简史 计算机语言发展历史
机器语言:简单理解就是0和1,但是由于其复杂难懂所以进化
汇编语言:将一些常用的机器语言整合,用助记符来代替机器语言那一大串0和1,但是流程控制都是用GOTO来进行,所以很混乱,需要继续进化
最后就是现在的高级语言了。
当然高级语言也分为强类型语言和弱类型语言,静态语言和动态语言
初学者不太需要知道这个概念,以后写多了慢慢了解。
简略讲一下就是
强类型就是一旦指定了类型,比如int i;之后要将i转换成byte类型或者String类型那么就需要强制转换。而弱类型则不用,会自动给转换。
静态类型就是一旦指定了一个类型那么他就是那个类型,动态类型就是比如JS中的var它会根据你给他的值来进行变换。
JAVA体系结构
==JavaSE:标准版,定位在个人计算机上的应用==
这个版本是Java平台的核心,他提供非常丰富的API来开发一般个人计算机上的应用程序。
==JavaEE:企业版,定位在服务器端的应用==
JavaEE是JavaSE的扩展,增加了用于服务器开发的类库。比如JDBC,在Java中使用SQL语法来访问数据库的数据,还有Servlet能够眼神服务器的功能,通过请求响应的模式来处理客户端的请求;JSP是一种可以将Java程序代码内嵌在网页内的技术等等。
==JavaME:微型版,定位在消费性电子产品的应用==
JavaME是JavaSE的内伸,包含J2SE的一部分核心类,也有自己的扩展类,增加了适合微小装置的类库:javax.microedition.io.*等。
Java核心机制 JAVA跨平台原理
首先有一个源文件.java文件(一写代码就是.java文件)然后用JDK编译之后就是.class文件然后就用JDK开始翻译到各个系统中。(虽说用java.exe,但实际上是用各个系统的JVM将字节码文件一行一行的解释成为当前操作系统认识的可执行文件)
数据类型 字面常量 一旦指定就不能被修改,一般用于通俗意义上的常量,比如一年有12个月这种类型,已经固定的量,这个时候我们的代码是用关键字final来修饰这个变量的,然后这个变量就成为了常量。(通常,常量用大写)
变量 变量声明格式:
数据类型 变量名 = 初始值;
==注意==如果只定义一个变量,没有给变量进行赋值的话,那么其实这个变量相当于没有定义,变量如果没有进行赋值的话,那么使用的时候就会出错,告诉你:尚未初始化变量。
变量不可以重复定义,但可以更改值
变量的作用域 :作用域是指作用范围,变量在什么范围中有效,作用范围就是离他最近的{}
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test { int b = 20 ; public static void main (String[] args) { System.out.println(a); int a = 10 ; System.out.println(a); System.out.println(b); { int a = 50 ; } public void eat () { int a = 30 ; } } }
基本数据类型
整数类型 整数类型有:
十进制整数
八进制整数:要求以0开头,比如015
十六进制整数:要求以0x开头或者0X开头
二进制:要求以0b或者0B开头:0b11
==注意==超范围会报错
浮点类型 十进制数形式:3.14,0.314
科学计数法形式:314e2(e大小写都行)
1 2 double f = 314e2 ;double f2 = 3143 (-2 );
字符类型 java中使用单引号表示字符常量,字符型在内存中占用2个字节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { public static void main (String[] args) { char ch1 = 'A' ; System.out.println(ch1+90 ); char ch2 = '\n' ; System.out.println("aaa" +ch2+"bbb" ); char ch3 = '2' + 2 ; System.out.println(ch3); char ch5 = ' ' ; System.out.println(ch5); } }
扩展:什么是编码?
生活中的案例就是初中早恋的场景
男生: 女生:
我想你–》1001 1001–》我想你
我爱你–》1011 1011–》我爱你
而这个1011和1001就是编码或者字符集
而有权威机构形成的编码表才可以称之为:字符集
常见的有ASCII,GB2312,GBK,Unicode,UTF-8
乱码现象就是由于选择编码方法的不匹配所导致的
布尔类型 boolean类型有两个常量值,true和false,在内存中占一位
一般用于判断逻辑条件。
基本数据类型的转换 类型转换就是在赋值运算或者算术运算的时候,要求数据类型一致,就要进行类型的转换。
类型转换费为两种:一种是自动转换,一种是强制转换。
内存演示:
1 2 3 4 5 6 7 8 public class test { public static void main (String[] args) { double d = 6 ; int i = (int ) 6.5 ; } }
Scanner的运用 1 2 3 4 5 6 7 8 9 10 11 12 13 public class test01_If { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请录入年龄" ); int age = sc.nextInt(); double height = sc.nextDouble(); String name = sc.next(); String sexStr = sc.next(); char sex = sexStr.charAt(0 ); } }
运算符 算术运算符 +,-,*,/,%,++(自增), - -(自减)
赋值运算符 =,+=,-=,*=,/=
关系运算符 大鱼号,小于号,==,大于等于,小于等于,!=
逻辑运算符 &,|,&&,||,!,^(异或)
位运算符 &,|,^,~,>>,<<,>>>(无符号右移,就是0顶替,有符号是 跟原来一样的顶替)
与,或,异或,非,右移,左移,无符号右移
流程控制 分支结构 IF
1 2 3 4 5 6 7 8 public class test01_If { public static void main (String[] args) { if (2 >1 ){ System.out.println("单分支" ); } } }
if ,else if,else
1 2 3 4 5 6 7 8 9 10 11 12 13 public class test01_If { public static void main (String[] args) { int a = 10 ; if (a<2 ){ System.out.println("单分支" ); }else if (a>10 ){ System.out.println("你好" ); }else { System.out.println("成功" ); } } }
随机数 直接用java中依靠的一个类:Math类生成,
这个类中专门用来生成随机数,返回的是【0.0,1.0)
所以一般我们用的时候都是用整数,所以需要强制转换,(int)(Math.random()*6+1)[1,6]
switch
语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class test01 { public static void main (String[] args) { switch (10 /10 ){ case 10 : case 9 : System.out.println("=9" ); break ; case 1 : System.out.println("=1" ); break ; default : System.out.println("算不出来" ); } } }
1、==switch后面是一个(),()表达式中返回的结果是一个等值,这个等值可以是int,byte,short,char,String,枚举类型==
2、==这个()中的等值会依次跟case后面的值比较,如果匹配成功就会执行:后面的代码(所有代码除非遇到break或者其他)
3、为了防止代码的穿透效果,每个分支后面会加上一个关键词break,遇到break这个分支就结束了
4、default类似于else如果没有匹配的值就会进行default后面的代码,若果在最后一行break可以不写,因为后面没有代码了。
5、swtich一般用于等值判断,并且等值情况比较小的情况。
循环结构 while
循环的四要素:1、条件初始化,2、条件判断,3、循环体4、迭代
1 2 3 4 5 6 7 8 9 10 11 12 public class test01_If { public static void main (String[] args) { int num = 1 ; int sum = 0 ; while (num<=5 ){ sum += num; num++; } System.out.println(sum); } }
do while
通常我会理解为先do后while,先把循环体执行一次,再开始循环,所以循环体至少被执行一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class test01 { public static void main (String[] args) { int i = 101 ; int sum = 0 ; do { sum += i; i++; }while (i<=100 ); System.out.println(i); System.out.println(sum); } }
什么场合适合do-while
while(考试是否通过){
考试;
}//这种就不适合用while,因为还没考试怎么能知道考试是否同通过呢?
而这时候适合用do-
for循环
1 2 3 4 5 6 7 8 9 10 11 public class test01 { public static void main (String[] args) { int sum = 0 ; for (int i = 0 ;i<=100 ;i++){ sum += i; } System.out.println(sum); } }
关键字 break,continue,return break关键字的作用是停止循环,停止的是最近的循环(作用域)
continue关键字的作用是:结束本次循环,继续下一次循环
return结束当前正在执行的方法,有时候会充当传输值的关键字。
方法的定义/调用/重载 方法的定义 方法就是用来完成一段特定功能的代码块
形式参数:在方法声明时用于接受外界传入的数据
实参:调用方法时实际传给方法的数据
返回值:方法执行完后返回调佣他的环境的数据
返回值类型:事先约定的返回值的数据类型,如果没有返回值,需要事先指定为void
1 2 3 4 5 6 7 8 9 10 public class test01 { public static int add (int num1,int num2) { return num1+num2; } public static void main (String[] args) { System.out.println(add(12 ,12 )); } }
方法的作用:可以把重复运用的代码块抽取出来,提高代码的复用性
==注意==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class test01 { public static void changeNum (int num1,int num2) { int t ; t = num1; num1 = num2; num2 = t; } public static void main (String[] args) { int a = 10 ; int b = 20 ; System.out.println("交换前两个数:" +a+b); changeNum(a,b); System.out.println("交换后两个数:" +a+b); } }
并没有实现数的交换,方法里只是实现了num1和num2的转换,但是在方法结束后,内存中的changNum方法就删除了,并没有实现数字的交换。
内存分析:首先内存中有栈,堆,方法区,一开始是进入到main方法然后内存会开辟一个main方法的栈帧,然后给a一个内存空间,给b一个,然后在栈里面开一个空间给changeNum,在changeNum里面完成了num1,和num2的交换但是并没有将值传回去,并没有实际修改a和b的数值,所以交换失败。
那么如何修改捏?
有两种方法:第一种就是直接在方法里打印交换的结果
第二种方法需要用到Integer了,后面再说~~😊
方法的重载 方法的重载是指一个类中可以定义多个方法名相同,但是参数不同的方法。调用时,会根据参数,自动匹配对应的方法。本质上是完全不同的方法,只是名称相同而已。
==条件:==
不同的含义:形参类型,形参个数,形参顺序,
返回值不同不能构成方法的重载,形参的名称不同也不能构成方法的重载。
数组 数组的定义:数组是相同类型数据的有序集合。数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。其中,每一个数据称作一个元素,每个元素可以通过一个索引(下标)来访问它们。
数组的四个基本特点:
1、长度是确定的
2、元素的类型必须是相同的类型,不允许出现混合的类型
3、数组的类型可以使==任何==的数据类型,包括基本数据类型和引用数据类型、
4、数组有索引:从0开始,到数组.length-1结束
5、数组变量属于引用类型,数组也是对象
扩展:数组变量属于引用类型,数组也是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他类型对象,数组对象本身是在堆中存放的。
数组学习:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class test01 { public static void main (String[] args) { int [] arr; arr = new int [4 ]; arr[0 ] = 4 ; System.out.println(arr[0 ]); System.out.println("数组的长度是:" +arr.length); } }
数组的遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class test { public static void main (String[] args) { int [] scores = {12 , 23 , 43 }; for (int i = 0 ; i <= scores.length; i++) { System.out.println(scores[i]); } int count = 0 ; for (int num : scores){ count ++; System.out.println(num); } } }
数组的三种初始化方式 1、静态初始化
直接在定义数组的同时就为数组元素分配空间并且赋值
1 2 3 4 5 6 7 public class test { public static void main (String[] args) { int [] arr = {12 ,12 ,12 }; } }
2、动态初始化
数组定义与为数组元素分配空间并且赋值的操作分开进行
1 2 3 4 5 6 7 8 9 public class test { public static void main (String[] args) { int [] arr ; arr = new int [3 ]; arr[0 ] = 12 ; } }
3、默认初始化 将数组进行初始化默认赋值为0
1 2 3 4 5 6 7 public class test { public static void main (String[] args) { int [] arr = new int [23 ]; } }
数组是引用类型,他的元素相当于类的实例变量,因此数组一经分配空间,其中每个元素也被按照实例变量同样的方式被隐式初始化。
数组的最值问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class test { public static void main (String[] args) { int [] arr = {12 ,2 ,34 ,21 ,23 ,4234 ,34 ,23 ,4 }; int maxNum = arr[0 ]; for (int i = 0 ;i<arr.length;i++){ if (arr[i]>maxNum){ maxNum = arr[i]; } } } }
将最大值的方法提取出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class test { public static void main (String[] args) { int [] arr = {12 ,2 ,34 ,21 ,23 ,4234 ,34 ,23 ,4 }; int num = getMaxNum(arr); System.out.println(num); } public static int getMaxNum (int [] arr) { int maxNum = arr[0 ]; for (int i = 0 ;i<arr.length;i++){ if (arr[i]>maxNum){ maxNum = arr[i]; } } return maxNum; } }
数组最大值的内存分析 ==方法的的实参传递给形参的时候一定要注意,一切都是值传递:
如果是基本数据类型,那么传递的就是字面值
如果是引用数据类型,那么传递的就是地址值。
首先是分为三个栈,堆,方法区
走到main方法里,然后会在栈里面给main方法开辟一个栈帧。
然后就到了arr的静态初始化,开辟了内存空间顺便也给里面的元素开辟了空间顺便进行了赋值操作。
然后就到了getMaxNum方法了,然后就需要在栈里面开辟内存空间,里面开辟了一个maxNum和arr的空间,因为arr是引用数据类型,所以传入的是arr的地址,在栈里面的也是arr的地址。
最后maxNum找到了最大的数4234然后将数值传给num之后这个方法就结束了,然后getNum这个方法就在内存中消失了。
数组查询问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class test { public static void main (String[] args) { int [] arr = {2 ,21 ,232 ,32 ,2 ,34234 ,1232 }; int index = -1 ; for (int i = 0 ;i<arr.length;i++){ if (arr[i] == 21 ){ index = i; break ; } } if (index != -1 ){ System.out.println(index); }else { System.out.println("达咩" ); } } }
添加元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class test { public static void main (String[] args) { int [] arr = {12 , 23 , 324 , 3 , 34 , 32 , 12 }; System.out.println("增加前的数组" ); for (int i = 0 ; i < arr.length; i++) { if (i != arr.length - 1 ) { System.out.print(arr[i] + "," ); } else { System.out.print(arr[i]); } } int index = 1 ; for (int i = arr.length - 1 ; i >= (index + 1 ); i--) { arr[i] = arr[i - 1 ]; } arr[index] = 66 ; for (int i = 0 ;i<arr.length;i++){ if (i!=arr.length-1 ){ System.out.print(arr[i]+"," ); }else { System.out.print(arr[i]); } } } }
删除元素 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class test { public static void main (String[] args) { int [] arr = {12 ,2 ,3 ,23 ,2 ,2 }; System.out.println("删除前的数组" ); Arrays.toString(arr); int index = -1 ; for (int i = 0 ;i<arr.length;i++){ if (arr[i] == 2 ){ index = i; break ; } } if (index != -1 ){ for (int i = index;i<arr.length-2 ;i++){ arr[i] = arr[i+1 ]; } arr[arr.length-1 ] = 0 ; }else { System.out.println("没有要删除的数组" ); } System.out.println(Arrays.toString(arr)); } }
详细描述main方法 1、main方法:程序的入口,在同一个类中,如果有多个方法,那么虚拟机就会识别 main方法,从这个方法作为程序的入口
2、main方法格式严格要求:public static void main(String[] args){}
public static :是修饰符
void:代表方法没有返回值,对应的类型为void
main:见名知意名字
String[] args 形参 不确定的因素
3、程序中是否可以有其他的方法也叫main方法?
可以,构成了方法的重载(重载就是方法名相同,形参列表不同,其实是另一个方法了只是名字一样罢了)。
4、形参为String[] 那么实参是什么?(实际传入的是什么呢?)
通过对于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestArray10 { public static void main (String[] args) { System.out.println(args.length); for (String str:args){ System.out.println(str); } } }
==//默认情况下,虚拟机在调用main方法的时候就是传入了一个长度为0的数组==
可变参数 可变参数就是提供了一个方法,参数的个数是可变的
int…num
double…num
boolean..,num类似于这样定义
可变参数是在JDK1.5之后加入的新特性
方法的内部对于可变参数处理的跟数组一样
可变参数和其他数据一起作为形参时,可变参数一定要放在最后
1 2 3 4 5 6 7 8 9 10 public class test { public static void main (String[] args) { } public static void method01 (int num2,int ...num) { } }
在写代码是建议不要使用可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class test { public static void main (String[] args) { method01(30 ,212 ,12 ,21 ,211 ,22 ,2 ,3 ,1 ); } public static void method01 (int num2,int ...num) { for (int i : num){ System.out.println(i+"\t" ); } System.out.println(); System.out.println(num2); } }
Arrays工具类 为了方便我们对数组进行操作,系统提供一个类Arrays,我们将它当做工具来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class test { public static void main (String[] args) { int [] arr = {12 ,2 ,21 ,2 ,3 ,43 ,43 }; System.out.println(Arrays.toString(arr)); Arrays.sort(arr); System.out.println(Arrays.toString(arr)); System.out.println(Arrays.binarySearch(arr,4 )); int [] arr2 = {2 ,2 ,23 ,3 ,4 }; int [] newArr = Arrays.copyOf(arr2,4 ); int [] newArry2 = Arrays.copyOfRange(arr2,1 ,4 ); int [] arr5 = {1 ,2 ,23 ,3 ,2 }; Arrays.fill(arr5,10 ); System.out.println(Arrays.toString(arr5)); } }
数组的复制操作 我们平时利用Arrays类的数组复制的方法来模仿他进行数组复制,所以简单了解一下。
从API那里看出Arrays类里面有一个arraycopy的静态方法就是用来进行数组复制的
static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
参数
src —原数组
srcPos – 原数组中的起始位置
dest —目标数组
destPos —目标数组中的起始位置
length – 要复制的数组元素的数量
二维数组 ==本质上全部都是一维数组==其实就是数组里面放的东西就是数组
1 2 3 4 5 6 7 8 9 10 11 12 13 public class test { public static void main (String[] args) { int [][] arr= new int [3 ][]; int [] a1 = {1 ,2 ,3 }; arr[0 ] = a1; arr[1 ] = new int []{12 ,23 ,4 }; arr[2 ] = new int []{43 ,342 ,2 }; } }
二维数组四种遍历方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class test { public static void main (String[] args) { int [][] arr = new int [3 ][]; int [] a1 = {1 , 2 , 3 }; arr[0 ] = a1; arr[1 ] = new int []{12 , 23 , 4 }; arr[2 ] = new int []{43 , 342 , 2 }; for (int i = 0 ; i < arr.length; i++) { for (int j = 0 ; j < arr[i].length; j++) { System.out.println(arr[i][j] + "\t" ); } System.out.println(); } for (int i = 0 ;i<arr.length;i++){ for (int num : arr[i]){ System.out.println(num +"\t" ); } System.out.println(); } for (int [] a : arr){ for (int num : a ){ System.out.println(num +"\t" ); } System.out.println(); } for (int [] a : arr){ for (int i = 0 ;i<a.length;i++){ System.out.println(a[i] + "\t" ); } System.out.println(); } } }
二维数组的初始化 还是三种 1、静态初始化 2、动态初始化 3、默认初始化
1、静态初始化
1 2 int [][] arr ={{12 ,32 },{123 ,34 ,4 }};int [][] arr = new int {{3 ,23 }}
2、动态初始化
1 2 3 int [][] arr = new int [3 ][];arr[0 ] = new int []{12234 ,324 ,43 }; arr[1 ] = new int []{23 ,33 ,4 ,3 };
3、默认初始化
面向对象 面向对象和面向过程主要的区别是考虑模式的区别,二者并不是对立的。
面向对象主要注重于谁来做
面向过程主要注重于怎么做
类和对象的关系 1、万事万物皆对象
2、
对象:具体的事物,模版下具体的产品
类:对 对象向上提取出抽象的部分,公共的部分,形成类,类是抽象的,是一个模板
3、一般写代码的时候先写类,再根据类创建对应的对象
面向对象的三个阶段
【1】面向对象分析OOA
对象:张三,李四,王五
抽取出一个类:人类
类里面有什么:
动态特性–》方法
静态特性-》属性
【2】面向对象设计OOD
先有类,再有对象
类:人类:Person
对象:zhangssan,lisi,wangwu
【3】面向对象编程OOP
创建类 1、 属性 2、 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Person { int age; String name; double height; double weight; public void eat () { int num = 10 ; System.out.println("我在吃饭" ); } }
创建对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class test { public static void main (String[] args) { Person zs = new Person (); zs.name = "张三" ; Person ls = new Person (); System.out.println(ls.age); } }
局部变量和成员变量 区别1:
成员变量:类中方法外定义的变量
局部变量:方法中定义的变量 代码块中定义的变量
区别2:代码中的作用范围
成员变量:当前类中的很多方法
局部变量:当前一个方法(当前代码块)
区别3:
成员变量有默认值
局部变量没有默认值
区别4:
成员变量不需要初始化而且并不建议初始化。
局部变量一定需要初始化,不然直接使用的时候报错
区别5:内存中的位置不同
成员变量在堆内存中
局部变量在栈内存中
区别6:作用时间不同
成员变量从对象从创建到销毁
局部变量:当前方法从开始到执行完毕
1 2 3 4 5 6 7 8 9 10 public class Student { int age ; public void study () { int num = 10 ; { int a ; } } }
构造器 在创建对象的时候:1、第一次遇到Person的时候,进行类的加载(只加载一次)
2、然后创建对象,为这个对象在堆中开辟空间(new出来的东西一般放在堆中)
3、为对象进行属性的初始化动作
new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器)
调用构造器的时候,如果类中没有写构造器,那么系统会默认给你分配一个构造器,只是我们看不见罢了,建议自己显示的写出来
构造器和方法的区别:
1、没有方法的返回值类型
2、方法体内部不能有return语句
3、构造器的名字必须跟类名一样
构造器的作用:不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并且属性有默认初始化的值。==调用构造器的目的是给属性进行赋值的操作==
注意:我们一般不会在空参构造器中进行初始化操作,因为那样的话每个属性的值就一样了, 实际上我们只用保证空参考构造器的存在就可以了,里面的东西不用写。
构造器的重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Student { int age ; public void study () { int num = 10 ; { int a ; } } public Student () { } public Student (int age) { this .age = age; } }
面向对象内存分析 1、
1 2 3 4 5 6 7 8 public class test { int id; int age; public static void main (String[] args) { Person p1 = new Person (); } }
第一次创建对象的时候会进行类的加载,然后将字节码信息放在方法区,然后如图所示了。
2、
1 2 3 4 5 6 7 8 9 public class test { int id; int age; public static void main (String[] args) { Person p1 = new Person (1 ,20 ,"oyy" ); } }
1 2 3 4 5 6 7 8 9 10 11 public class Person { int id; int age; String school; public Person (int a ,int b,String c) { id =a; age = b; school = c; } }
首先是main方法开辟栈帧然后因为这次构造器不是空参构造器了 ,所以会开辟一个Person构造器栈帧。首先先加载字节码信息然后到堆里面有个默认值,然后就到了构造器栈帧给堆里面的属性赋值,赋值完毕后,构造器就被回收了,字符串第一次出现会放在字符串常量池,以后就会直接在字符串常量池里面取了。
this 1、在创建对象的过程中
(1)在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
(2)创建对象,在堆中开辟空间
(3)对对象进行初始化操作,属性赋值都是默认的初始值
(4)new关键字调用构造器,执行构造方法,在构造器中对属性重新进行赋值。
this指代的就是当前对象。
2、this的用法
(1)this 可以修饰属性
当属性名字和形参发生重名的时候,或者属性名字和局部变量重名的时候,就会发生就近原则,所以如果我要是直接使用变量名字的话就是离得近的那个形参或者局部变量,这个时候要是想要表示属性的话就加上this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Person { int age; String name; double height; public Person () { } public Person (int age,String name,double height) { this .age = age; this .name = name; this .height = height; } public void eat () { int age = 10 ; System.out.println(age); System.out.println(this .age); System.out.println("我喜欢吃饭" ); } }
(2)this 可以修饰方法
在同一个类中,方法可以互相调用,this可以省略不写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Person { int age; String name; double height; public Person () { } public Person (int age,String name,double height) { this .age = age; this .name = name; this .height = height; } public void play () { eat(); System.out.println("上网" ); System.out.println("洗澡" ); } public void eat () { System.out.println(age); System.out.println("吃饭" ); } }
(3)this可以修饰构造器
同一个类中的构造器可以相互用this调用,this修饰的构造器必须放在第一行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class Person { int age; String name; double height; public Person () { } public Person (int age,String name,double height) { this (age,name); this .height = height; } public Person (int age,String name) { this (age); this .name = name; } public Person (int age) { this .age = age; } public void play () { eat(); System.out.println("上网" ); System.out.println("洗澡" ); } public void eat () { System.out.println(age); System.out.println("吃饭" ); } }
为什么要用this 来修饰构造器呢?我认为在一个类中如果有很多个构造器的话,在调用构造器的时候,其实已经给我们调用了其他的构造器,我们写this的时候只是将他显示的写出来
static 1、static可以修饰:属性、方法、代码块、内部类
在类加载的时候,会将静态内容也加载到方法区的静态域中,静态的内容先于对象存在这个静态内容被所有该类的对象共享
static 修饰属性:一般官方的推荐访问方式:类名.属性名去访问
1 2 3 4 5 6 7 8 9 10 11 12 public class test { int id; static int sid; public static void main (String[] args) { test t1 = new test (); t1.id = 10 ; test.sid = 10 ; } }
static 应用场景:某些特定的数据想要在内存中共享,只有一块,这个情况就可以用staitc修饰属性
属性分为:
静态属性
非静态属性
2、static修饰方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Demo { int id; static int sid; public void a () { System.out.println(id); System.out.println(sid); System.out.println("------a" ); } static public void b () { System.out.println(sid); System.out.println("------b" ); } public static void main (String[] args) { Demo d = new Demo (); d.a(); Demo.b(); d.b(); } }
静态的东西是最先加载的,所以非静态的东西在静态的东西里面调用不了,因为还没加载出来调用不了(我是这么想的)😊😼
代码块 1、类的组成:属性,方法,构造器,代码块,内部类、
2、代码块分为:普通块、构造块、静态块、同步块(多线程)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Test { int a; static int sa; public void a () { System.out.println("-----a" ); { System.out.println("这是普通块" ); System.out.println("----000000" ); int num = 10 ; System.out.println(num); } } public static void b () { System.out.println("------b" ); } { System.out.println("------这是构造块" ); } static { System.out.println("-----这是静态块" ); System.out.println(sa); b(); } public Test () { System.out.println("这是空构造器" ); } public Test (int a) { this .a = a; } public static void main (String[] args) { Test t = new Test (); t.a(); Test t2 = new Test (); t2.a(); } }
普通块的目的是限制局部变量的作用范围,放在方法中,主要是为了方便看清作用域 ,
构造块是写在外面的里面有具体的功能,主要是为了分区域吧我认为
静态块中只能调用静态属性和静态方法,也是为了分清静态区域
代码执行的顺序:
最先静态块,然后到构造块,然后到构造器,然后到方法中的普通块
包,import ==包的作用是为了解决重名问题(实际上包对应的就是盘符上的目录)解决权限问题==
创建包
名字全部小写
中间用.隔开
一般用公司域名倒着写
加上模块的名字
不能使用系统中的关键字
包的声明位置一般在非注释的第一行
1 2 3 4 5 package com.msb7;import com.msb2.Person; import java.util.Date;
(1)==使用不同包下的类要需要导包: import *. .*; 例如:import java.util.Date;== (2)在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包。 (3)同一个包下的类想使用不需要导包,可以直接使用。 (4)==在java.lang包下的类,可以直接使用无需导包:==
静态导入
1 2 import static java.lang.Math.*;
在静态导入后,同一个类中有相同的方法的时候,会优先走自己定义的方法
1 2 3 4 5 6 7 8 9 10 11 12 public class Test { public static void main (String[] args) { System.out.println(random()); System.out.println(PI); System.out.println(round(5.6 )); } public static int round (double a) { return 1000 ; } }
三大特性 封装、继承、多态
封装 :就是public private protected,将属性私有化,其他人不能随意获取这个属性,增加了安全性,即便我们可以通过方法可以访问属性,但是也不能随意访问,因为我们可以在方法中加入限制条件。(就比如家里装了一把锁,虽然也能撬开,但是并不能随意让别人进出家门,增加了安全性)
继承 :关键字extends
继承的好处就是提高代码的复用性
父类定义的内容,子类直接拿过来用就可以了,不用代码上的反复重复定义了
一个父类可以有多个子类,一个子类只能有一个直接父类但是可以间接继承自其他类
Object类是所有类的根基父类,所有类都直接或者间接继承自Object类
==权限范围图==
方法的重写(区别于重载) 重写:子类和父类中,当子类的方法对父类的方法不满意的时候,要对父类的方法进行重写。
重写有严格的格式要求 :子类方法名字和父类必须一致,参数列表也要和父类一致。
super super可以修饰属性和方法
在子类方法中可以通过super.属性,super.方法,显示的去调用父类提供的属性,方法。在通常情况,super.可以省略不写。
super修饰构造器:
其实我们平时写构造器的时候第一行都有:super() –>作用是调用父类构造器,只是我们一般省略不写(在子类中写)
在构造器中,super调用的父类构造器和this调用子类构造器只能存在一个,两者不能共存,因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行;。
Object类 所有类都直接或者间接继承自Object类,Object类是所有Java类的根基类,也就意味着所有的java对象都拥有Object类的属性和方法。
如果在类的声明未使用extends关键字声明其父类,则默认继承Object类。
Object类中的toString()的作用。
1 2 3 4 5 6 7 8 @Override public String toString () { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}' ; }
Object类中的equals()方法 equlas()比较的是内容是否相等
==比较的是地址是否相等
我们一般不会直接使用父类的方法,而是在子类中对这个方法进行重写
instanceof instanceof是用来判断是否是这个类的实例
a instanceof b //判断a对象是否是b这个类的实例,如果是返回true,不是返回false
类和类的关系 1、继承关系通常用extends
2、实现关系 implements
3、依赖关系
4、关联关系
5、聚合关系
6、组合关系
前面两种关系是代码层面的关系,后面四种是语义层面的关系并不是很好的可以准确的区分,了解即可。
多态 多态指的是方法的多态,而不是属性的多态
多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。
比如动物有叫的方法,猫类继承动物类,狗类继承动物类,但是猫和狗教的方法是不一样的。
多态的好处:提高代码的扩展性,符合面向对象的原则:开闭原则,扩展式开放的,修改是关闭的。
重点理解
1 2 Pig p = new Pig ();Animal an = p;
将上面的代码合为一句话: Animal an = new Pig(); =左侧:编译期的类型 =右侧:运行期的类型
多态使用非常常见的场合:父类当方法的形参,传入的是具体的子类对象
然后调用同一个方法,根据传入的不同子类展现不同的效果,这样就构成了多态。
1 2 3 public void play (Animal an) { an.shout(); }
内存分析
首先加载new出来的Pig放在方法区,然后给p一个内存空间在栈里开辟空间。
然后又给an在栈里面开辟空间,因为左边是编译时期的类型是动物,后面是运行期的类型,所以实际上他是一只猪,然后运行的时候条用的是shout()方法,子类pig重写了Animal的方法,所以调用的时候走的是子类PIG的喊叫方法。
向下转型,向上转型
报错原因:
1、an在编译器的时候是Animal类型,在Animal中没有eat方法,也没有weight属性,这个属性是在Pig中。
2、an只能看到0x99空间中的Animal中的部分内容
编译器的时候p其实还是Animal所以它的地址其实还是0x99,但是运行的时候,可以调用Pig的方法(前提是Animal里面和Pig是相同的,即Pig重写了Animal的的方法。
但是这个时候实在是想要访问Pig的属性和方法该怎么办捏?
这个时候就需要转型啦~。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Demo { public static void main (String[] args) { Pig p = new Pig (); Animal an = p; an.shout(); Pig pig = (Pig)an ; pig.eat(); pig.age = 10 ; pig.weight = 60.8 ; } }
之前的equals方法也是利用了向下转型的方法来获取子类中特有的内容进行比较。
扩展:
多态的使用有很多种情况,不仅可以使用父类方法做形参,还可以使用父类方法做方法的返回值类型,其返回值类型可以是任意一个子类对象
简单工厂模式的实现,他是解决大量对象创建的一个解决方案,将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的使用要求是:
定义一个static方法,通过类名直接调用
返回值是父类类型,返回的可以是其任意子类类型
传入一个字符串型的参数,工厂根据参数创建对应的子类产品。
final 用法1:final修饰的值,变量值不可以改变,这个变量也变成了一个字符常量,预定俗称,常量大写
用法2:final修饰引用数据类型,那么地址值不可以改变
1 2 final Dog d = new Dog ();
用法3:
1 2 3 4 5 6 7 8 9 10 11 12 final Dog d2 = new Dog (); a(d2); b(d2); } public static void a (Dog d) { d = new Dog (); } public static void b (final Dog d) { }
final修饰方法 :用final修饰的方法,那么这个方法不可以被子类重写。
final修饰类 :final修饰类,代表没有子类,该类不可以被继承:
一旦一个类被final修饰,那么里面的方法也没有必要用final修饰(final省略不写)
==那为什么要用final修饰类呢==
举个例子你就知道了:
JDK提供的Math类:使用Math类的时候无需导包,直接使用就可以了,Math没有子类,不被其他类继承。
里面的属性全部被final修饰,方法也是被fianl修饰的,只是省略不写了,因为子类没有必要进行重写,因为比如PI什么的是固定的,就是不希望别人去修改它。
其次外界不可以创建对象,,Math类中的所有属性,方法都被static修饰,那么不用创建对象去调用,只能通过类名.属性名,类名.方法名 去调用。
这里的话static哪里也有提到静态导入
抽象类,抽象方法 抽象类和抽象方法的关系就像类和方法的关系差不多。
抽象类的作用:
在抽象类中定义抽象方法,目的是为了为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容。抽象类设计避免了子类设计的随意性,通过抽象类,子类的设计变得更加严格,进行某些程度上的限制。使子类更加的通用。
抽象类中可以有抽象方法,和正常的方法。
在一个抽象类中,子类会有一类方法,子类对这个方法非常满意,无需重写,直接使用(正常的方法),但是总有一些方法是子类对这个方法永远不满意,就会对这个方法进行重写,那么这个时候就需要用抽象方法了,一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了抽象方法。
注意事项:
1、如果这个类中有方法是抽象方法,那么这个类也要变成一个抽象类。
2、抽象类可以被其他类继承,一个类继承抽象类,那么这个类可以变成抽象类
3、一般子类不会加abstract修饰,一般会让子类重写父类中的方法,子类继承抽象类必须重写全部的抽象方法。
4、如果子类没有重写全部的抽象方法,那么子类也可以变成一个抽象类。
5、抽象类不可以创建对象。
6、既然抽象类不可以创建对象,但是可以创建继承抽象类的子类,然后利用多态的写法,父类是编译时期类型,子类是运行期类型。
==子类创建对象的过程中一定要调用父类的构造器==
所以由于这个原因,从抽象类即使不能创建对象,也要有构造器给子类调用!!!!!!!!!!!!
抽象类是否可以被final修饰?
不能被final修饰,因为抽象类的初衷就是给予子类继承用的。要是被final修饰了这个抽象类,就不存在继承了,就没有子类。
接口 类和接口是同一概念
接口中没有构造器
接入如何声明:interface
在JDK1.8之前,接口之中只有两个部分内容:
(1)常量:固定修饰符:public static final
(2)抽象方法:固定修饰符:public static final
注意:修饰符可以省略不写,IDE会帮你自动补全,但是初学者建议写上,防止遗忘
1 2 3 4 5 6 7 8 9 10 11 12 public interface TestInterface { int NUM = 10 ; void a () ; } interface TestInterface { void e () ; void f () ; }
类和接口的关系是什么?
实现关系 类实现接口:
一旦实现一个接口,那么实现类要重写接口中的全部的抽象方法:
如果没有重写全部方法,那么这个类可以成为一个抽象类
java 只有单继承,java还有多实现
一个类继承其他类,可以实现多个接口
1 2 3 4 5 6 public class student extends Person implements TestInterface { @Override public void a () { System.out.println("---1" ); } }
写法通常是先继承再实现
接口不能创建对象
当然也还有多态的写法就是
1 TestInterface02 t = new Student ();
接口中的常量如何访问?
1 2 3 4 5 6 7 System.out.println(TestInterface01.NUM); System.out.println(Student.NUM); Student s = new Student (); System.out.println(s.NUM); TestInterface01 t2 = new Student (); System.out.println(t2.NUM);
接口的作用是什么?
定义规则!那么他跟抽象类不同的地方在哪?他是接口不是类
接口定义好规则之后,实现类负责实现即可。(不需要继承还要重写方法)
继承:子类对父类的继承
实现:实现类对接口的实现
多态的应用场合:
(1)父类当做方法的形参,传入具体的子类对象
(2)父类当做方法的返回值,返回的是具体的子类的对象
(3)接口当做方法的形参,传入的是具体的子类的对象
(4)接口当做方法的返回值,返回的是具体的实现类对象
接口和抽象类的区别:
抽象类:1、用abstract修饰 2、抽象类不能实例化,即不能用new关键字来实例化对象 3、含有抽象方法的类是抽象类必须用abstract修饰 4、抽象类可以含有抽象方法也可以不包含抽象方法,抽象类中可以有具体的方法 5、如果一个子类实现父类(抽象类)的所有抽象方法,子类可以不是抽象类,否则就是抽象类 6、抽象类中的抽象方法只有方法体,没有具体实现。
接口:1、接口用interface修饰 2、接口不能被实例化 3、一个类只能继承一个类,但是可以实现多个接口 4、接口中都是抽象方法 5、接口中不能包含实力域或静态方法(静态方法必须实现,接口中的方法是抽象方法,不能实现)
JDK1.8之后,接口中多加了一个内容:
1、被public default 修饰的非抽象方法
==default必须加上==,实现类中要是想要重写接口中的非抽象方法,那么default必须不能加,否则会出错
实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加否则出错。
(抽象方法就是给别人重写的,如果写了个非抽象方法,直接给别人的话,那么就不要加default)
(2)静态方法:
接口中的静态方法static 不能省略不写,其次静态方法不能重写
为什么要在接口中加入非抽象方法呢?
因为如果接口中只能定义抽象方法的话,那么我要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受到影响
现在在接口中加入非抽象方法,对现实类没有影响,想调用就去调用即可。
内部类 成员内部类 类的组成有:属性,方法,构造器,代码块(普通块,静态块,构造块,同步块),内部类
一个类中内部还有一个类,那么这个类叫内部类,当然还有外部类(就是内部类的外面一层就是外部类)
内部类:成员内部类(静态的,非静态的)和 局部内部类 (位置:方法内,块内,构造器内)
成员内部类:里面有属性,方法,构造器等。
局部内部类 在局部内部类中访问到的变量必须是被final修饰的
如果一个类在整个项目中只使用一次,那么就没必要单独创建一个类,使用内部类就可以了。
匿名内部类,本质上就是一个没有名字的局部内部类,定义在代码块中,方法中
作用:方便创建子类对象,最终目的是简化代码的编写
格式:
1 2 3 new 类|抽象类名或者接口名(){ 重写方法; }
1 2 3 4 5 6 7 8 9 public Comparable method3 () { return new Comparable (){ @Override public int compareTo (Object o) { return 200 ; } }; }
异常 Exception:在程序运行过程中,发生了不正常的现象,阻止了程序的运行,我们称之为发生异常
可以用if-else来弥补漏洞但是代码臃肿,业务代码和异常代码混在一起,可读性差,程序员需要花费大量的时间来维护这个漏洞,程序员很难堵住所有漏洞。
由于if-else处理异常缺点太多,所以java专门处理可一个异常处理机制:try-catch-finally
异常出现了之后会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class test { public static void main (String[] args) { try { Scanner sc = new Scanner (System.in); int num1 = sc.nextInt(); int num2 = sc.nextInt(); }catch (Exception ex){ System.out.println("对不起程序出现异常" ); } } }
原理:
把可能出现的异常代码放在try代码块中,然后将异常封装为对象,被catch后面的()中的那个异常对象接收:接受以后执行catch后面的代码,然后后面的代码并不会影响
详细说:
1、try中没有异常,catch中的代码不执行
2、try中有异常。catch进行捕获:
如果catch中异常类型和你出的类型匹配的话:走catch中的代码—》进行捕获
如果catch中的异常类型和你出的异常类型不匹配:不走catch中的代码–》没有捕获成功,程序相当于遇到异常了,中断了,后面的代码不执行
catch中如何处理异常 第一种:异常捕获成功后,catch中什么都不写,什么都不做
第二种:输出自定义异常信息
第三种:打印异常信息
(1)调用toString方法,显示异常的类名
1 2 System.out.println(ex); System.out.println(ex.toString());
(2)显示异常描述信息对应的字符串,如果没有显示就是null
1 System.out.println(ex.getMessage());
(3)显示异常的堆栈信息:将异常信息捕获后,在控制台将异常信息给我们展示出来,方便我们查看异常。
(4)抛出异常
try-catch-finally 怎么样才可以将try-catch后面的代码必须执行?
只要将必须执行的代码放入finally中,那么这个代码无论如何一定执行。
return和finally执行顺序
先执行finally最后执行return
什么代码会放在finally中呢
关闭数据库资源,关闭IO流资源,关闭socket资源
有一句代码很厉害,他可以让finally中代码不执行!
#### 多重catch
1、try中出现异常后,将异常类型跟catch后面的类型依次比较,按照代码的顺序进行比对,执行第一个与异常类型匹配的catch语句
2、一旦执行其中一条catch语句之后,后面的catch语句就会被忽略了!
3、在安排catch语句的顺序的时候,一般会将特殊异常放在前面(并列),一般化异常放在后面
先写子类异常,在写父类异常
在JDK1.7之后,异常处理方式:可以用并用|符号:
1 2 3 catch (IllegalArgumentException | NullPointerException){}
异常的分类
异常我没写全,懒得写啦嘿嘿
注意:程序中语法错误,逻辑错误,都不属于上面的Error,Exception
正常检查异常有三种方法:
1、try-catch嵌套try-catch
2、多重catch
3、throws
throw和throws的区别 1、位置不同:
throw在方法内部
throws在方法签名处,方法声明处
2、内容不同:
throw + 异常对象(检查异常,运行时异常)
thows + 异常类型(可以多个类型,用,拼接)
3、作用不同:
thow:异常出现的源头,制造异常
thows:在方法声明处,告诉方法的调用者,告诉方法的调用者,这个方法中可能出现我声明的这些异常。然后调用者对这个异常进行处理:要么自己处理要么在继续向外抛出异常。
重载和重写的异常
复习一下:重载是在同一个类中有相同的方法名,但是形参列表不同,这种方法叫重载
重写是子类对于父类不满意的方法进行重写,要求形参列表和名字必须是一样的
样例:
1、重载
1 2 3 4 5 6 7 8 public class Demo { public void a () throws Exception{ } public void a (int age) throws ArithmeticException{ } }
2、重写
1 2 3 4 5 6 7 8 9 10 11 12 public class Person { public void eat () throws RuntimeException{ System.out.println("父类方法" ) } } class Student extends Person { public void eat () throws Exception{ System.out.println("子类方法" ) } }
自定义的异常可以继承:运行时异常
1 2 3 4 5 6 7 8 9 10 public class MyException extends RuntimeException { static final long serialVersionUID = -70348971907L ; public MyException () { } public MyException (String msg) { super (msg); } }
也可以继承检查异常:
1 2 3 4 5 6 7 8 9 public class MyException extends Exception { static final long serialVersionUID = -70348971907L ; public MyException () { } public MyException (String msg) { super (msg); } }
如果继承的是运行时异常,那么在使用中无需额外处理
如果继承的是检查异常,那么使用的时候需要try-catch-捕获或者throws向上抛
常用类 包装类 什么是包装类:以前定义变量经常使用基本数据类型
对于基本数据类型来说,他就是一个数,加点属性,加点方法,加点构造器,将基本数据类型对应进行了一个封装,产生了一个新的类 –》包装类。
已经有了基本数据类型,为什么要封装为包装类?
1、java语言 买你想对象的语言,最擅长操作各种各样的类
2、以前学习装数据的–》数组
以后学习的装数据的—》集合,有一个特点,只能装引用数据类型的数据
4、是不是有了包装类以后就不用基本数据类型了?不是
这边用Integer类进行案例讲解:
1、直接引用,无需导包:
java.lang包下的类Integer
实现了Comparable接口
这个类被final修饰,那么这个类不能有子类,不能被继承
包装类是对基本数据类型的封装,对int类型封装产生了Integer
里面的属性:
1 2 3 4 5 System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MIN_VALUE); System.out.println(Integer.MAX_VALUE+1 ); System.out.println(Integer.MIN_VALUE-1 );
构造器(发现没有空参构造器)
(1)int类型作为构造器的参数:
1 Integer i1 = new Integer (12 );
里面的构造器是这么写的
1 2 3 public Integer (int value) { this .value = value ; }
当然你传入String类型的时候会给你自动转换成Int类型但是转换不行的话就会报错了
包装类特有的机制: 自动装箱,自动拆箱
1 2 3 4 5 6 7 Integer i = 12 ; System.out.println(i); Integer i2 = new Integer (12 ); int num = i2; System.out.println(num);
(1)自动装箱 自动拆箱 是从JDK1.5后新出的特性
(2)自动装箱 自动拆箱 : 将基本数据类型和包装类进行快速的类型转换
类里面肯定是有方法的:
常用的方法:
compareTo(),equals(),intValue():作用将Integer—>int
parseInt(String s) : String –>int:
toString():Integer –>String
日期相关的类(后面再写) java,util,Date类 Math类 也是在java.lang包下的一个类,直接使用,无需导包
用final修饰的类,这个类不能被继承
其次 构造器被私有化,不能创建Math类的对象:
Math内部的所有属性,方法都被static修饰:类名.直接调用,无需创建对象
常用方法:
Math.random随机数、Math.abs绝对值、Math.ceil向上取值、Math.floor向下取值、Math.round四舍五入
详细说说Random类
Math.random方法是返回带正号的double值、该值大于0.0并且小于1.0
==利用带参数的构造器创建对象==
1 2 Random r1 = new Random (System.currentTimeMillis());int i = r1.nextInt();
==利用空参构造器创建对象==
1 2 3 Random r2 = new Random ();r2.nextInt(10 ); r2.nextDouble();
String类 也是在java.lang包下的一个类,直接使用,无需导包。
形象的说一下字符串:
“abc”就是String类下的一个具体的对象
字符串是不可变的:
这个String类不可以被继承,不能有子类:
String底层就是一个char类型的数组
StringBuilder类 字符串的分类:
1、不可变字符串:String
2、可变字符串:StringBuilder、StringBuffer
什么叫可变什么是不可变,并且有什么区别呢?
StringBuilder底层:非常重要的两个属性
1 2 char [] value;int count;
对应内存分析:
1 2 3 4 5 6 7 8 9 10 11 12 public class Test01 { public static void main (String[] args) { StringBuilder sb3 = new StringBuilder (); StringBuilder sb2 = new StringBuilder (3 ); StringBuilder sb = new StringBuilder ("abc" ); System.out.println(sb.append("def" ).append("aaaaaaaa" ).append("bbb" ).append("ooooooo" ).toString());; } }
1 StringBuilder sb = new StringBuilder ("abc" );
可变和不可变 String就是不可变:在地址不变的情况下,想把“abc”变成“abcdef”是不可能的
StringBuilder可变:可变,在StringBuilder这个对象的地址不变的情况下,想把“abc”变成“abcdf”是可能的,直接追加就可以了
StringBuilder常用方法:
.append增加、.delete删除.deleteCharAt()、.insert(3,”,”)插入、replace(3,5,“我好累”)替换、.substring(2,4)查找
StringBuffer常用方法: 差不多,懒得写了
特别重要!!String、StringBuilder、StringBuffer 区别和联系 1、String类是不可变类、即一旦一个String对象被创建后,包含在这个对象中的字符序列是不可改变的,制止这个对象销毁。
2、StringBuffer类则代表一个字符序列可变的字符串,可以通过append、insert、reverse、setCharAt、setLength等方法改变其内容。一旦生成了最终的字符串,调用toString方法将其转变为String
3、JDK1.5新增了一个StringBuilder类和StringBuffer相似,构造方法和方法基本相同。不同的是StrtingBuffer是线程安全的,而StringBuilder是线程不安全的,所以性能略高,通常情况下,创建一个内容可变的字符串,应该优先考虑使用StringBuilder。
StringBuilder:JDK1.5开始 效率高 线程不安全
StringBuffer:JDK1.0开始 效率低 线程安全
集合 什么是算法和数据结构 1、算法
(1)可以解决具体问题:例如 从1加到100
解题流程=算法
(2)有设计解决的具体的流程
算法1:一个一个加
算法2:高斯算法
(3)有评价这个算法的具体的 指标 –》时间复杂度和空间复杂度
2、数据结构:就是在计算机的缓存,内存和硬盘如何组织管理数据的。重点在结构上,是按照什么结构来组织管理我们的数据。
数据结构分为:
(1)逻辑结构:–》思想上的结构–》卧室、厨房、卫生间–》线性表(数组、链表),图,树、栈、队列
(2)物理结构:–》真是结构–》钢筋混凝土+牛顿力学—-》紧密结构(顺序结构),跳转结构(链式结构)
线性表:是几个n个类型相同数据元素的有序序列
特点:1、相同数据类型 2、序列(顺序性) 3、有限
逻辑结构和物理结构的关系:
线性表逻辑结构,对应真是结构如果是紧密结构–》典型就是数组
数组的优点:查找元素快 缺点:删除和增加元素效率低
线性表逻辑结构,对应的真是结构如果是跳转结构—》链表:
优点:删除元素,插入元素效率高
缺点:查询效率低
集合引入 1、数组,集合都是对多个数据进行存储操作的,简称为容器。
PS:这里的存储指的是内存层面的存储,而不是持久化存储
2、数组:特点:
(1)数组一旦指定了长度,那么长度就被确定,不可以更改。
(2)删除,增加元素 效率低
(3)数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取
(4)数组存储:有序,可重复,对于无序,不可重复的数组不能满足需求。
4、正因为上面的缺点,引入了一个新的存储数据的结构—》集合
有很多集合,每个集合底层的数据结构不一样。集合不一样,特点也不一样。
集合分为两大阵营
集合应用场景:
前后端数据交互的时候:需要将相同数据的个体整合到一起的时候需要集合。
Collection接口的常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class Test01 { public static void main (String[] args) { Collection col = new ArrayList (); col.add(18 ); col.add(12 ); col.add(11 ); col.add(17 ); System.out.println(col); List list = Arrays.asList(new Integer []{11 , 15 , 3 , 7 , 1 }); col.addAll(list); System.out.println(col); System.out.println(col); System.out.println("集合中元素的数量为:" +col.size()); System.out.println("集合是否为空:" +col.isEmpty()); boolean isRemove = col.remove(15 ); System.out.println(col); System.out.println("集合中数据是否被删除:" +isRemove); Collection col2 = new ArrayList (); col2.add(18 ); col2.add(12 ); col2.add(11 ); col2.add(17 ); Collection col3 = new ArrayList (); col3.add(18 ); col3.add(12 ); col3.add(11 ); col3.add(17 ); System.out.println(col2.equals(col3)); System.out.println(col2==col3); System.out.println("是否包含元素:" +col3.contains(117 )); } }
总结一下:首先是接口不能创建对象,利用实现类创建对象,
集合有一个特点:只能存放引用数据类型的数据,不能是基本数据类型
基本数据类型放入到集合里面会自动装箱。
集合的遍历 迭代器!!!!!!
1 2 3 4 Iterator it = col.iterator();while (it.hasNext()){ sout(it.next()); }
List接口 List接口的常用方法和遍历方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public class Test03 { public static void main (String[] args) { List list = new ArrayList (); list.add(13 ); list.add(17 ); list.add(6 ); list.add(-1 ); list.add(2 ); list.add("abc" ); System.out.println(list); list.add(3 ,66 ); System.out.println(list); list.set(3 ,77 ); System.out.println(list); list.remove(2 ); System.out.println(list); list.remove("abc" ); System.out.println(list); Object o = list.get(0 ); System.out.println(o); System.out.println("---------------------" ); for (int i = 0 ;i<list.size();i++){ System.out.println(list.get(i)); } System.out.println("---------------------" ); for (Object obj:list){ System.out.println(obj); } System.out.println("---------------------" ); Iterator it = list.iterator(); while (it.hasNext()){ System.out.println(it.next()); } } }
ArrayList实现类(动态数组): ArrayList实现List接口的失误:集合创始人 承认了这个失误,但是在后续的版本中没有删除,觉得没必要(JDK1.7)。(实现了两次List接口,这个是失误,但是没大问题)
底层 :是一个数组,数组类型是Object类型,size是数组中的有效数据
在JDK1.7中:调用构造器的时候给底层数组elementData初始化,数组初始化长度为10:
对应内存:
调用add方法:
1 2 3 ArrayList al = new ArrayList ();System.out.println(al.add("abc" )); System.out.println(al.add("def" ));
当数组中10个位置都满了的时候就开始进行数组的扩容,扩容长度为原数组的1.5倍,将elementData的指向从老数组指向新数组,然后将老数组的内容赋值到新数组中,然后返回新数组。
JDK1.8的ArrayList实现类
JDK1.8底层依旧是Object类型的数组,size数组中的有效长度
在JDK1.8中,在调用空构造器的时候,底层elementData数组初始化为{}.
Vector实现类(也是动态数组) 底层是Object数组,int类型属性表示数组中的有效长度,底层数组长度是10,但是在扩容的时候底层数组扩容长度是2倍
泛型 什么是泛型(Generic)
泛型相当于标签
形式:<>
集合容器类在设计阶段、声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object类型,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他部分是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型
如果不是用泛型的话:
一般我们在使用的时候基本上往集合中存入相同类型的数据–》便于管理,所以现在引用数据类型都可以存入集合,不方便!
1 2 3 4 5 6 7 8 9 public class Test01 { public static void main (String[] args) { ArrayList<Integer> al = new ArrayList <Integer>(); al.add(98 ); } }
使用了泛型以后,可以确定集合中存放数据的类型,在编译时期就可以检查出来
使用泛型的时候你可能觉得麻烦,实际使用了泛型才会简单,后续遍历等操作简单
泛型的类型:都是引用数据类型,不能是基本数据类型。
(6)ArrayList al = new ArrayList();在JDK1.7以后可以写为: ArrayList al = new ArrayList<>(); —<> —-钻石运算符
自定义泛型结构 泛型类、泛型接口 现在创造一个泛型类,Test就是一个泛型类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test <E>{ int age; String name ; E sex; public void a (E n) { } } class Test { public static void main (String[] args) { Test t = new Test (); t.a("abc" ); Test<String> t2 = new Test <>(); t2.sex = "男" ; t2.a("abc" ); } }
继承情况:
1、父类指定泛型
1 2 3 4 5 6 7 8 9 10 class SubTest extend Test<Integer>{} class Demo { public static void main (String[] args) { SubTest t = new SubTest (); t.a(19 ); } }
2、父类不指定泛型
如果父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候可以确定
1 2 3 class SubTest2 <E> extends Test <E>{ }
1 2 3 4 5 6 7 class Demo2 { public static void main (String[] args) { SubTest<String> s = new SubTest <>(); s.a("abc" ); s.sex="女" ; } }
3、细节:
泛型类可以定义多个参数类型
1 2 3 4 5 6 7 8 public class Test <A,B,C>{ A age; B name; C sex; public void a (A m,B n,C x) { } }
PS:泛型类的构造器的写法
这么写
3、不同的泛型的引用类型不可以相互赋值
1 2 3 4 5 public void b () { ArrayList<String> list1 = null ; ArrayList<Integer> list2 = null ; list1 = list2; }
4、泛型如果不指定,那么就会被擦除,反应对应的类型为Object类型:
1 2 3 Test t1 = new Test ();t1.a("abc" ); t1.a(17 );
5、泛型类的静态方法不能使用这个类的泛型
6、不能直接使用E[]的创建:
泛型方法 什么是泛型方法: 不是带泛型的方法就是泛型方法
==泛型方法也有要求:这个方法的泛型的参数要和当前的类的泛型无关==
换个角度:泛型方法对应的那个泛型参数类型 和当前所在的这个类 是否是泛型类 ,泛型是啥 无关
==泛型方法定义的时候,前面要加上==
==原因:如果不加的话,会把T当做一种数据类型,然而代码中没有T就会报错==
3、==T的类型是在调用方法的时候确定的==
4、泛型方法是否可以是静态方法?可以是静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Test <E>{ public static void a (E e) { } public static <T> void b (T t) { } } class Demo { public static void main (String[] args) { Test<String> t = new Test <>(); t.a("abc" ); } }
泛型参数存在继承关系的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Test { public static void main (String[] args) { Object obj = new Object (); String s = new String (); obj = s; Object[] objArr = new Object [10 ]; String[] strArr = new String [10 ]; objArr = strArr; List<Object> list1 = new ArrayList <>; List<String> list22 = new ArrayList <>; list1 = list2; } }
泛型不具备继承性,但是数据具备。
通配符 1、在没有通配符的时候:下面的a 方法,相当于方法的重复定义,报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Test { }
引入通配符:
1 2 3 4 5 6 7 8 9 10 11 12 public class Demo { public static void main (String[] args) { List<Object> list1 = new ArrayList <>(); List<String> list2 = new ArrayList <>(); List<Integer> list3 = new ArrayList <>(); List<?> list = null ; list = list1; list = list2; list = list3; } }
发现:A和B是子类父类的关系,G 和G不存在子类父类关系,是并列的加入通配符 ? 后,G<?>就变成了G和G的父类。
通配符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test { public void a (List<?> list) { for (Object a : list){ System.out.println(a); } } } class T { public static void main (String[] args) { Test t = new Test (); t.a(new ArrayList <Integer>()); t.a(new ArrayList <String>()); t.a(new ArrayList <Object>()); } }
使用通配符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Test { public void a (List<?> list) { for (Object a : list){ System.out.println(a); } list.add(null ); Object s = list.get(0 ); } } class T { public static void main (String[] args) { Test t = new Test (); t.a(new ArrayList <Integer>()); t.a(new ArrayList <String>()); t.a(new ArrayList <Object>()); } }
泛型受限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Test { public static void main (String[] args) { List<Object> a = new ArrayList <>(); List<Person> b = new ArrayList <>(); List<Student> c = new ArrayList <>(); List<? extends Person > list1 = null ; List<? super Person> list2 = null ; list2 = a; list2 = b; list3 = c; } }
LinkedList实现类的使用(链表) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class Test { public static void main (String[] args) { LinkedList<String> list = new LinkedList <>(); list.add("aaaaa" ); list.add("bbbbb" ); list.add("ccccc" ); list.add("ddddd" ); list.add("eeeee" ); list.add("bbbbb" ); list.add("fffff" ); list.addFirst("jj" ); list.addLast("hh" ); list.offer("kk" ); list.offerFirst("pp" ); list.offerLast("rr" ); System.out.println(list); System.out.println(list.poll()); System.out.println(list.pollFirst()); System.out.println(list.pollLast()); System.out.println(list.removeFirst()); System.out.println(list.removeLast()); System.out.println(list); System.out.println("---------------------" ); for (int i = 0 ;i<list.size();i++){ System.out.println(list.get(i)); } System.out.println("---------------------" ); for (String s:list){ System.out.println(s); } System.out.println("---------------------" ); for (Iterator<String> it = list.iterator();it.hasNext();){ System.out.println(it.next()); } } }
模拟LinkedList 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Node { int val ; Node next ; } public class MyLinkedList { Node first; Node last; int count = 0 ; public MyLinkedList () { } public void add (Object o) { if (first == null ){ Node n = new Node (); } } }
Linked源码解析 JDK1.7和1.8的源码是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class LinkedList <E>{ transient int size = 0 ; private static class Node <E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this .item = element; this .next = next; this .prev = prev; } } transient Node<E> first; transient Node<E> last; public LinkedList () { } public boolean add (E e) { linkLast(e); return true ; } void linkLast (E e) { final Node<E> l = last; final Node<E> newNode = new Node <>(l, e, null ); last = newNode; if (l == null ) first = newNode; else l.next = newNode; size++; modCount++; } public int size () { return size; } public E get (int index) { checkElementIndex(index); return node(index).item; } Node<E> node (int index) { if (index < (size >> 1 )) { Node<E> x = first; for (int i = 0 ; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1 ; i > index; i--) x = x.prev; return x; } } }
面试题:iterator(),iterable,iterator关系 ArraList集合 实现 List接口 继承 Collection接口 继承 Iterable接口(接口里面有一个抽象方法iterator()。(抽象方法要在具体的实现类(ArrayList)中实现)而在ierator()方法里面返回值为Iterator接口
2、hasNext(),next()具体实现:
3、增强for循环 底层也是通过迭代器熟实现的
ListIterator迭代器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Test2 { public static void main (String[] args) { ArrayList<String> list = new ArrayList <>(); list.add("aa" ); list.add("bb" ); list.add("cc" ); list.add("dd" ); list.add("ee" ); Iterator<String> it = list.iterator(); while (it.hasNext()){ if ("cc" .equals(it.next())){ list.add("kk" ); } } } }
这行代码看着没什么错但是还是报错了!
出错原因:就是迭代器和list同事对集合进行操作:
list指针指到相应位置可是it的位置还是没变。
解决办法:事情让一个人做:引入新的迭代器ListIterator迭代和添加都是靠ListIterator完成的:
Set接口 HashSet实现类应用(哈希表) 1、放入Integer类型数据:
1 2 3 4 5 6 7 8 9 10 11 public class test { public static void main (String[] args) { HashSet<Integer> hs = new HashSet <>(); System.out.println(hs.add(19 )); hs.add(5 ); hs.add(20 ); System.out.println(hs.size()); } }
哈希表的特点是唯一,无序
2、HashSet原理图:(简要原理图)
疑问: 1.数组的长度是多少。 2.数组的类型是什么? 3.hashCode,equals方法真的调用了吗?验证 4.底层表达式是什么? 5.同一个位置的数据 向前放 还是 向后放? 6.放入数组中的数据,是直接放的吗?是否封装为对象了?
LinkedHashSet使用 其实就是在HashSet的基础上,多了一个总的链表,这个总的链表将放入的元素串在一起,方便有序的遍历:
特点也是唯一无序
比较器的使用 1、以int类型为案例:
比较思路:将比较的数据作差,然后返回一个int 类型的数据,将这个int类型的数据 按照 =0,》0 《0来比较判断
2、比较string 类型的数据:
string类实现了comparable接口,这个接口中有一个抽象方法compareTo, string类中重写这个方法即可
3、比较double类型的数据:
也是利用compareTo这个方法。
4、比较自定义的数据类型:
(1)内部比较器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Student implements Comparable <Student>{ private int age; private double height; private String name; puiblic int getAge () { return age; } @Override public int compareTo (Student o) { return this .getName().compareTo(o.getName()); } }
2、外部比较器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BiJiao01 implements Comparator <Student> { @Override public int compare (Student o1, Student o2) { return o1.getAge()-o2.getAge(); } } class BiJiao02 implements Comparator <Student> { @Override public int compare (Student o1, Student o2) { return o1.getName().compareTo(o2.getName()); } }
外部比较器和内部比较器 谁好呀? 答案:外部比较器,多态,扩展性好
TreeSet实现类(树) 特点:唯一、无序(没有按照输入顺序进行输出,有序(按照生序进行遍历)
原理:底层:二叉树(数据结构中的一个逻辑结构)
TreeSet底层的二叉树的遍历是按照升序结果出现的,这个升序是靠中序遍历得到的。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Map接口(图) 1、常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class Test01 { public static void main (String[] args) { Map<String,Integer> map = new HashMap <>(); System.out.println(map.put("lili" , 10101010 )); map.put("nana" ,12345234 ); map.put("feifei" ,34563465 ); System.out.println(map.put("lili" , 34565677 )); map.put("mingming" ,12323 ); System.out.println(map.size()); System.out.println(map); System.out.println(map.containsKey("lili" )); System.out.println(map.containsValue(12323 )); Map<String,Integer> map2 = new HashMap <>(); System.out.println(map2.put("lili" , 10101010 )); map2.put("nana" ,12345234 ); map2.put("feifei" ,34563465 ); System.out.println(map2.put("lili" , 34565677 )); map2.put("mingming2" ,12323 ); System.out.println(map==map2); System.out.println(map.equals(map2)); System.out.println("判断是否为空:" +map.isEmpty()); System.out.println(map.get("nana" )); System.out.println("-----------------------------------" ); Set<String> set = map.keySet(); for (String s:set){ System.out.println(s); } System.out.println("-----------------------------------" ); Collection<Integer> values = map.values(); for (Integer i:values){ System.out.println(i); } System.out.println("-----------------------------------" ); Set<String> set2 = map.keySet(); for (String s:set2){ System.out.println(map.get(s)); } System.out.println("-----------------------------------" ); Set<Map.Entry<String, Integer>> entries = map.entrySet(); for (Map.Entry<String, Integer> e:entries){ System.out.println(e.getKey()+"----" +e.getValue()); } } }
2、TreeMap 1、key的类型为string类型:
1 2 3 4 5 6 7 public static void main (String[] args) P{ Map<String,Integer> map = new TreeMap <>(); map.put("blili" ,1234 ); map.put("alili" ,2345 ); System.out.println(map.size()); System.out.println(map); }
2、key类型是一个自定义的引用数据类型
(1)内部比较器:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test03 { public static void main (String[] args) { Map<Student,Integer> map = new TreeMap <>(); map.put(new Student (19 ,"blili" ,170.5 ),1001 ); map.put(new Student (18 ,"blili" ,150.5 ),1003 ); map.put(new Student (19 ,"alili" ,180.5 ),1023 ); map.put(new Student (17 ,"clili" ,140.5 ),1671 ); map.put(new Student (10 ,"dlili" ,160.5 ),1891 ); System.out.println(map); System.out.println(map.size()); } }
Student类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class Student implements Comparable <Student>{ private int age; private String name; private double height; public int getAge () { return age; } public void setAge (int age) { this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public double getHeight () { return height; } public void setHeight (double height) { this .height = height; } public Student (int age, String name, double height) { this .age = age; this .name = name; this .height = height; } @Override public String toString () { return "Student{" + "age=" + age + ", name='" + name + '\'' + ", height=" + height + '}' ; } @Override public int compareTo (Student o) { return this .getName().compareTo(o.getName()); } }
(2)外部比较器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test03 { public static void main (String[] args) { Map<Student,Integer> map = new TreeMap <>(new Comparator <Student>() { @Override public int compare (Student o1, Student o2) { return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight())); } }); map.put(new Student (19 ,"blili" ,170.5 ),1001 ); map.put(new Student (18 ,"blili" ,150.5 ),1003 ); map.put(new Student (19 ,"alili" ,180.5 ),1023 ); map.put(new Student (17 ,"clili" ,140.5 ),1671 ); map.put(new Student (10 ,"dlili" ,160.5 ),1891 ); System.out.println(map); System.out.println(map.size()); } }
这里的外部比较器有点丑。
3、源码部分 Collections工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test { public static void main (String[] args) { ArrayList<String> list = new ArrayList <>(); list.add("cc" ); list.add("bb" ); list.add("aa" ); Collections.addAll(list,"ee" ,"dd" ,"ff" ); Collections.addAll(list,new String []{"gg" ,"oo" ,"pp" }); System.out.println(list); Collections.sort(list); System.out.println(list); System.out.println(Collections.binarySearch(list, "cc" )); ArrayList<String> list2 = new ArrayList <>(); Collections.addAll(list2,"tt" ,"ss" ); Collections.copy(list,list2); System.out.println(list); System.out.println(list2); Collections.fill(list2,"yyy" ); System.out.println(list2); } }
Map部分整体结构图
集合补充
数据结构:栈 数据结构分为:
(1)逻辑结构
IO流 File类 在java中要操作文件/目录怎么办?
盘符上的文件封装为对象,对象属于File类的对象,有了这个对象我们就可以直接操纵这个对象。
对文件进行操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Test01 { public static void main (String[] args) throws IOException { File f1 = new File ("d://test.txt" ); File f2 = new File ("d:" +File.separator+"test.txt" ); System.out.println("文件是否可读:" +f1.canRead()); System.out.println("文件是否可写:" +f1.canWrite()); System.out.println("文件的名字:" +f1.getName()); System.out.println("上级目录:" +f1.getParent()); System.out.println("是否是一个目录:" +f1.isDirectory()); System.out.println("是否是一个文件:" +f1.isFile()); System.out.println("是否隐藏:" +f1.isHidden()); System.out.println("文件的大小:" +f1.length()); System.out.println("是否存在:" +f1.exists()); System.out.println("绝对路径:" +f1.getAbsolutePath()); System.out.println("相对路径:" +f1.getPath()); System.out.println("toString:" +f1.toString()); } }
对目录进行操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Test01 { public static void main (String[] args) throws IOException { File f = new File ("d://test" ); System.out.println("文件是否可读:" +f.canRead()); System.out.println("文件是否可写:" +f.canWrite()); System.out.println("文件的名字:" +f.getName()); System.out.println("上级目录:" +f.getParent()); System.out.println("是否是一个目录:" +f.isDirectory()); System.out.println("是否是一个文件:" +f.isFile()); System.out.println("是否隐藏:" +f.isHidden()); System.out.println("文件的大小:" +f.length()); System.out.println("是否存在:" +f.exists()); System.out.println("绝对路径:" +f.getAbsolutePath()); System.out.println("相对路径:" +f.getPath()); System.out.println("toString:" +f.toString()); File f2 = new File ("D:\\test\\a\\b\\c" ); f2.delete(); String[] list = f.list(); for (String name : list){ System.out.println(name); } System.out.println("=========================" ); File[] files = f.listFiles(); for (File file:files){ System.out.println(file.getName()+"," +file.getAbsolutePath()); } } }
IO流 1、File类:封装文件、目录信息,对文件、目录进行操作,但是我们不可以获取文件、目录中的内容。
2、所以我们需要 IO流
I/O:Input/Output 的缩写,用于处理设备之间的数据传输。
3、形象理解:IO流 当做一根管子:
案例通过java程序完成文件的复制 一个字符一个字符的将文件中的内容读取到程序中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test01 { public static void main (String[] args) throws IOException { File f = new File ("d:\\test.txt" ); FileReader fr = new FileReader (f); int n; while ((n=fr.read())!=-1 ){ System.out.print((char )n); } fr.close(); } }
想一次性读取五个字符,不够的话下次再读五个字符:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test01 { public static void main (String[] args) throws IOException { File f = new File ("d:\\test.txt" ); FileReader fr = new FileReader (f); char [] ch = new char [5 ]; int len = fr.read(ch); while (len != -1 ) { System.out.println("长度为:" +len); for (int i = 0 ; i < len; i++) { System.out.print(ch[i]); } System.out.println(); len = fr.read(ch); } fr.close(); } }
多线程 分清概念(程序 、进程、线程)
程序:是一段静态的代码(程序是静态的)
进程:是一次完整的执行过程,。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。(进程是动态的)它包括了一个从产生,存在到消亡的过程
线程:进程可以进一步细化为线程,是一个程序内部的一条执行的路径,如果一个进程同一时间并行执行多个线程,就是支持多线程的
通俗理解:一个人为了完成吃饭的任务(一个进程),一个人先吃菜(一个线程)再吃饭(另一个线程),然后另一个人是狂吃!!!饭和菜我全部一口给吃下去!!(多线程同时执行)!!!!
单核cpu和多核cpu的任务执行 单核cpu :cpu在执行的时候是按照时间片执行的,一个时间片只能执行一个线程,因为时间片时间特别短,感受的是“同时”执行多个线程,实际上这个多线程是假的!
多核cpu :多个cpu就能实现真正意义上的一个时间片多线程同时执行
并行和并发 并行: 多个cpu同时执行多个任务
并发: 一个cpu“同时”执行多个任务(采用时间片切换)
创建线程的三种方式 第一种 继承Thread类 在学多线程之前,以前的代码也不是单线程的。以前也是有三个线程同时执行的
一个处理异常的线程,main方法的线程,垃圾收集器的线程
那么现在我想自己创一个线程该怎么办
有一个线程类——》创造一个线程对象
但是不是说名字中带着线程的名字就有抢线程的能力了,需要继承一个Tread类,并且需要重写Tread类中的run方法,逻辑卸载run方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package tread;public class MyTread extends Thread { @Override public void run () { for (int i = 1 ; i <= 10 ; i++) { System.out.println(i); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package tread;public class Test { public static void main (String[] args) { for (int i = 1 ;i<=10 ;i++){ System.out.println("main1----" +i); } MyTread mt = new MyTread (); mt.start(); for (int i = 1 ;i<=10 ;i++){ System.out.println("main2----" +i); } } }
设置读取线程的名字 利用this.getName()方法来读取线程名字
利用Tread.currentThread()来获取当前正在执行的线程
利用.setName()来获取线程的名字
或者利用类中父类中的有参构造器来设置线程的名字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package tread;public class MyTread extends Thread { @Override public void run () { for (int i = 1 ; i <= 10 ; i++) { System.out.println(this .getName()+i); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test { public static void main (String[] args) { for (int i = 1 ; i <= 10 ; i++) { Thread.currentThread().setName("主线程1" ); System.out.println(Thread.currentThread().getName() + i); } MyTread mt = new MyTread (); mt.setName("子线程" ); mt.start(); for (int i = 1 ; i <= 10 ; i++) { System.out.println((Thread.currentThread().getName() + i)); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package tread;public class MyTread extends Thread { public MyTread () { super ("子线程" ); } @Override public void run () { for (int i = 1 ; i <= 10 ; i++) { System.out.println(this .getName()+i); } } }
第二种 实现 Runnable接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package tread;public class MyThread2 implements Runnable { @Override public void run () { for (int i = 0 ; i < 10 ; i++) { System.out.println(i); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package tread;public class Test { public static void main (String[] args) { for (int i = 1 ; i <= 10 ; i++) { Thread.currentThread().setName("主线程1" ); System.out.println(Thread.currentThread().getName() + i); } MyTread mt = new MyTread (); mt.setName("子线程" ); MyThread2 mt2 = new MyThread2 (); Thread t = new Thread (mt2,"子线程2" ); t.start(); mt.start(); for (int i = 1 ; i <= 10 ; i++) { System.out.println((Thread.currentThread().getName() + i)); } } }
第三种:实现Callable接口 对比第一种和第二种方法,两种方法都需要一个run方法
但是run方法有不足:
1、没有返回值
2、不能抛出异常
实现Callable接口好处:有返回值,能抛出异常但是缺点就是创建线程比较麻烦
1 2 3 4 5 6 7 8 9 10 11 12 public class MyThread3 implements Callable <Integer> { @Override public Integer call () throws Exception { return new Random ().nextInt(10 ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test { public static void main (String[] args) throws ExecutionException, InterruptedException { MyThread3 trn = new MyThread3 (); FutureTask ft = new FutureTask (trn); Thread t = new Thread (ft); t.start(); Object obj = ft.get(); System.out.println(obj); } }
具体callable接口的使用方法可以参考下面这个链接:
Callable接口详解_callable接口的作用-CSDN博客
线程的生命周期
线程的常见方法 1、设置优先级 优先级越高与可能被调用
线程对象.setPriority(1到10);
2、join方法: 当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程。
注意:必须先start,再join才有效。
3、sleep 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Test02 { public static void main (String[] args) { DateFormat df = new SimpleDateFormat ("HH:mm:ss" ); while (true ){ Date d = new Date (); System.out.println(df.format(d)); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class TestThread extends Thread { @Override public void run () { for (int i = 1 ; i <= 1000 ; i++) { System.out.println("子线程----" +i); } } } class Test { public static void main (String[] args) { TestThread tt = new TestThread (); tt.setDaemon(true ); tt.start(); for (int i = 1 ; i <= 10 ; i++) { System.out.println("main---" +i); } } }
4、stop 1 2 3 4 5 6 7 8 9 10 11 public class Demo { public static void main (String[] args) { for (int i = 1 ; i <= 100 ; i++) { if (i == 6 ){ Thread.currentThread().stop(); } System.out.println(i); } } }
线程安全问题 方法1:同步代码块 反射 通过案例体会反射的好处 案例:美团外卖 —->付款 —-》要么用微信支付 要么用支付宝支付
美团外卖接口
1 2 3 4 5 public interface Mtwm { void payOnline () ; }
微信类
1 2 3 4 5 6 7 public class WeChat implements Mtwm { @Override public void payOnline () { System.out.println("我已经点了外卖,正在使用微信支付" ); } }
支付宝类
1 2 3 4 5 6 7 public class AliPay implements Mtwm { @Override public void payOnline () { System.out.println("我已经点了外卖,我正在使用支付宝进行支付" ); } }
银行卡类
1 2 3 4 5 6 public class BankCard implements Mtwm { @Override public void payOnline () { System.out.println("我已经定了外卖,我正在用招商银行信用卡支付" ); } }
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test { public static void main (String[] args) { String str = "微信" ; if ("微信" .equals(str)){ pay(new WeChat ()); } if ("支付宝" .equals(str)){ pay(new AliPay ()); } if ("招商银行" .equals(str)){ pay(new BankCard ()); } } public static void pay (WeChat wc) { wc.payOnline(); } public static void pay (AliPay ap) { ap.payOnline(); } public static void pay (BankCard bc) { bc.payOnline(); } }
为了提高代码的扩展性—-》面向对象特性:多态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Test { public static void main (String[] args) { String str = "微信" ; if ("微信" .equals(str)){ pay(new WeChat ()); } if ("支付宝" .equals(str)){ pay(new AliPay ()); } if ("招商银行" .equals(str)){ pay(new BankCard ()); } } public static void pay (Mtwm m) { m.payOnline(); } }
多态确实可以提高代码的扩展性,但是:扩展性没有达到最好。 怎么没有达到最好:上面的分支,还是需要手动的删除或者添加。 解决办法:反射机制 利用反射实现上述功能:
1 2 3 4 5 6 7 8 9 10 11 public class Demo { public static void main (String[] args) throws Exception { String str = "com.bitzh.test01.AliPay" ; Class cls = Class.forName(str); Object o = cls.newInstance(); Method method = cls.getMethod("payOnline" ); method.invoke(o); } }
反射概念 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象, 都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在编译后产生字节码文件的时候,类加载器子系统通过二进制字节流,负责从文件系统加载class文件。 在执行程序(java.exe)时候,将字节码文件读入JVM中—->这个过程叫做类的加载。然后在内存中对应创建一个java.lang.Class对象—>这个对象会被放入字节码信息中,这个Class对象,就对应加载那个字节码信息,这个对象将被作为程序访问方法区中的这个类的各种数据的外部接口。 所以:我们可以通过这个对象看到类的结构,这个对象就好像是一面镜子,透过镜子看到类的各种信息,我们形象的称之为反射 这种“看透”class的能力(the ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语。
说明:在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。 如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
Class类的理解
获取字节码信息的四种方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class Test { public static void main (String[] args) throws ClassNotFoundException { Person p = new Person (); Class c1 = p.getClass(); System.out.println(c1); Class c2 = Person.class; System.out.println(c2); System.out.println(c1==c2); Class c3 = Class.forName("com.bitzh.test02.Person" ); ClassLoader loader = Test.class.getClassLoader(); Class c4 = loader.loadClass("com.bitzh.test02.Person" ); } }
获取运行时 类的完整结构 获取构造器和对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class Test01 { public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class cls = Student.class; Constructor[] c1 = cls.getConstructors(); for (Constructor c:c1){ System.out.println(c); } System.out.println("-------------------" ); Constructor[] c2 = cls.getDeclaredConstructors(); for (Constructor c:c2){ System.out.println(c); } System.out.println("-------------------" ); Constructor con1 = cls.getConstructor(); System.out.println(con1); Constructor con2 = cls.getConstructor(double .class, double .class); System.out.println(con2); Constructor con3 = cls.getDeclaredConstructor(int .class); System.out.println(con3); Object o1 = con1.newInstance(); System.out.println(o1); Object o2 = con2.newInstance(180.5 , 170.6 ); System.out.println(o2); } }
获取属性和对属性赋值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Test02 { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException { Class cls = Student.class; Field[] fields = cls.getFields(); for (Field f:fields){ System.out.println(f); } System.out.println("---------------------" ); Field[] declaredFields = cls.getDeclaredFields(); for (Field f:declaredFields){ System.out.println(f); } System.out.println("---------------------" ); Field score = cls.getField("score" ); System.out.println(score); Field sno = cls.getDeclaredField("sno" ); System.out.println(sno); System.out.println("---------------------" ); System.out.println(Modifier.toString(sno.getModifiers())); Class clazz = sno.getType(); System.out.println(clazz.getName()); String name = sno.getName(); System.out.println(name); System.out.println("-------------------------------" ); Field sco = cls.getField("score" ); Object obj = cls.newInstance(); sco.set(obj,98 ); System.out.println(obj); } }
获取方法和调用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class Test03 { public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class cls = Student.class; Method[] methods = cls.getMethods(); for (Method m:methods){ System.out.println(m); } System.out.println("-----------------------" ); Method[] declaredMethods = cls.getDeclaredMethods(); for (Method m:declaredMethods){ System.out.println(m); } System.out.println("-----------------------" ); Method showInfo1 = cls.getMethod("showInfo" ); System.out.println(showInfo1); Method showInfo2 = cls.getMethod("showInfo" , int .class, int .class); System.out.println(showInfo2); Method work = cls.getDeclaredMethod("work" ,int .class); System.out.println(work); System.out.println("-----------------------" ); System.out.println(work.getName()); int modifiers = work.getModifiers(); System.out.println(Modifier.toString(modifiers)); System.out.println(work.getReturnType()); Class[] parameterTypes = work.getParameterTypes(); for (Class c:parameterTypes){ System.out.println(c); } Method myMethod = cls.getMethod("myMethod" ); Annotation[] annotations = myMethod.getAnnotations(); for (Annotation a:annotations){ System.out.println(a); } Class[] exceptionTypes = myMethod.getExceptionTypes(); for (Class c:exceptionTypes){ System.out.println(c); } Object o = cls.newInstance(); myMethod.invoke(o); System.out.println(showInfo2.invoke(o,12 ,45 ));; } }
获取类的接口,所在包,注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Test04 { public static void main (String[] args) { Class cls = Student.class; Class[] interfaces = cls.getInterfaces(); for (Class c:interfaces){ System.out.println(c); } Class superclass = cls.getSuperclass(); Class[] interfaces1 = superclass.getInterfaces(); for (Class c:interfaces1){ System.out.println(c); } Package aPackage = cls.getPackage(); System.out.println(aPackage); System.out.println(aPackage.getName()); Annotation[] annotations = cls.getAnnotations(); for (Annotation a:annotations){ System.out.println(a); } } }