JavaSE
前言
计算机基础
计算机---->Computer:有软件和硬件的组合
冯.诺依曼体系结构:输入设备,存储器,输出设备,运算器,控制器
计算机常用快捷键:Ctrl+C复制 Ctrl+V粘贴 Ctrl+A全选 Ctrl+X剪切 Ctrl+Z撤销 Ctrl+S保存 Ctrl+D
博客的重要性
一、需要总结和思考
二、提升文笔组织能力
三、提升学习总结能力
四、提升逻辑思维能力
五、帮助他,结交朋友
学习java方法
多写(代码),多写(笔记),多写(文章),多练(交流),多练(思维),多练(技能),多分享(知识),多提问(怎么了),多思考(为什么),最重要(坚持)
1、Java帝国的诞生
1972年C诞生:贴近硬件,运行极快,效率极高。操作系统,编译器,数据库,网络系统等。指针和内存管理。 1982年C++诞生:面向对象。兼容C。图形领域、游戏等
Java初生 Java2标准版(J2SE):去占领桌面 Java2移动版(J2ME):去占领手机 Java2企业版(J2EE):去占领服务器
Java发展 构建工具: Ant,Maven,Jekins 应用服务器: Tomcat,Jetty,Jboss, Websphere, weblogic Web开发: Struts,Spring,Hibernate, myBatis 开发工具: Eclipse,Netbean,intellij idea,Jbuilder ...... 2006 : Hadoop(大数据领域) 2008 : Android(手机端)
2、Java特性和优势
简单性 面向对象 可移植性 高性能 分布式 动态性 多线程 安全性 健壮性
3、JDK、JRE、JVM
JDK : Java Development KitJRE : Java Runtime Environment JVM : JAVA Virtual Machine
4、卸载&&安装JDK
1.删除Jawa的安装目录 2.删除JANA_HOME 3.删除path下关于Jawa的目录 4.java -version
1.百度搜索JDK8,找到下载地址 2.同意协议 3.下载电脑对应的版本 4.双击安装JDK 5.记住安装的路径 6.配置环境变量 1.我的电脑-->右键-->属性 2.环境变量->JAVA_HOME 3.配置path变量 7.测试JDK是否安装成功 1.打开cmd 2.java -version
5、HelloWorld
1.随便新建一个文件夹,存放代码 2.新建一个Java文件 。文件后缀名为.javao Hello.java 。【注意点】系统可能没有显示文件后缀名,我们需要手动打开 3.编写代码
public class Hello{ public static void main(String[] args){ System.out.println("Hello world"); } }
4.编译javac java文件,会生成一个class文件 5.运行class文件,java class文件
可能会遇到的情况 1.每个单词的大小不能出现问题,Java是大小写敏感的 2.尽量使用英文 3.文件名和类名必须保证一致。并且首字母大写 4.符号使用的了中文
程序运行机制
6、Java基础语法
6.1 Java注释
注释并不会被执行,是给我们写代码的人看的。 平时写代码一定要注意规范。书写注释是一个非常好的习惯!
-
单行注释//
-
多行注释/**/
-
文档注释/** * */
有趣的代码注释
/** ************************************************************** * * * .=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-. * * | ______ | * * | .-" "-. | * * | / \ | * * | _ | | _ | * * | ( \ |, .-. .-. ,| / ) | * * | > "=._ | )(__/ \__)( | _.=" < | * * | (_/"=._"=._ |/ /\ \| _.="_.="\_) | * * | "=._"(_ ^^ _)"_.=" | * * | "=\__|IIIIII|__/=" | * * | _.="| \IIIIII/ |"=._ | * * | _ _.="_.="\ /"=._"=._ _ | * * | ( \_.="_.=" `--------` "=._"=._/ ) | * * | > _.=" "=._ < | * * | (_/ \_) | * * | | * * '-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=' * * * * LASCIATE OGNI SPERANZA, VOI CH'ENTRATE * ************************************************************** */
6.2 标识符
标识符是为类名、方法、变量或者其它用户定义的名称。 标识符可以有一个或者多个字符,在Java中,标识符的构成规则如下: a. 标识符由数字(0-9)和字母(a-z或A-Z)、美元符号($)、下划线(_)构成,每个符号之间没有空格。 b. 不能以数字开头。 c. 严格区分大小写。如aa和Aa 是两个完全不同的标识符。 还有就是命名标识符的时候应该遵循语义的表达、增强代码的可读性。 如表示姓名标识符,通过使用 name,而不是abc、xxx。 还有就是当标识符由多个字母组成的时候,一般遵循驼峰命名法。 myFirstName
public class test{ public static void main(String[] args){ String 王者="百星王者"; System.out.println(王者); } }
-
关键字
关键字就是对编译器有特殊意义的固定单词,不能在程序中做为标识符使用的。 a. 数据类型相关的 byte / short / int / long / float / double / char / boolean / class / interface; b. 流程控制相关的 if / else / do / while / for / switch / case / default / break / continue / return / try / catch / finally; c. 修饰符相关的 public / private / protected / final / void / static / abstract / synchronized; d. 动作相关的 package / import / throw / extends / implements / this / super / new; e. 保留字 true / false / null
6.3 数据类型
强类型语言:要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用 弱类型语言:js
Java的数据类型分为两大类
-
基本类型(primitive type)
-
引用类型(reference type)
Java数据类型图
注意点:float数值加F/f,long数值加L/l
什么是字节? 位(bit):是计算机内部数据储存的最小单位,11001100是一个八位二进制数。 字节(byte)∶是计算机中数据处理的基本单位,习惯上用大写B来表示, 1B(byte,字节) = 8bit(位) 字符:是指计算机中使用的字母、数字、字和符号 字节(Byte)是计算机信息技术用于计量存储容量的一种计量单位,也表示一些计算机编程语言中的数据类型和语言字符。一个字节存储8位无符号数,储存的数值范围为0-255。如同字元一样,字节型态的变数只需要用一个位元组(8位元)的内存空间储存。
数据类型扩展 八进制0/十六进制0x float 字长是有限,离散,舍入误差,大约数 BigDecimal 数学工具类,银行业务 少用浮点数来比较大小,因为有误差。 转义字符:\t制表符 \n换行
public class test{ public static void main(String[] args){ int i2=0x10; int i1=010; float f=0.1f; double d=1.0/10; System.out.println(f==d)//false float d1=2312312312313311f; float d2=d1+1; System.out.println(d1==d2)//true char c='中';//Unicode编码 System.out.println((int)c);//所有的字符本质都是数字 } }
6.4 类型转换
强转转化:(int)变量;取值范围大的数据,给取值范围小的数据,不能直接赋值,需要强制转换; 1.不能对Boolean值进行转换 2.不能把对象类型转换为不相干的类型 3.把高容量转换到低容量的时候强制转换 4.转换的时候可能存在内存溢出,或是精度问题
自动转化:低到高,自动 注意:转化可能内存溢出,精度问题
类型从低到高
低------------------------------------------>高 byte,short,char-->int-->long-->float-->double
public class test{ public static void main(String[] args){ //精度问题 System.out.println((int)34.8f); //JDK7新特行,数字之间可以使用下划线分隔 int money=10_0000_0000; //内存溢出 System.out.println(money*20); } }
6.5 变量、常量、作用域
Java变量(可以变化的量):程序中最基本的存储单元其要素包括变量名,变量类型和作用域。 boolean:默认值false,其他出基本类型外,一律默认值为null
注意事项: 每个变量都有类型,类型可以是基本类型,也可以是引用类型。 变量名必须是合法的标识符。 变量声明是一条完整的语句,因此每一个声明都必须以分号结束
public class test{ //静态代码块 static{ //可以定变量 } static int allClicks=0;//类变量 String str="hello world !"//实例变量 public static void main(String[] args){ int i=0;//局部变量 } }
Java常量(Constant):初始化(initialize)后不能在改变值!不会变动的值。 常量名一般使用大写字母。 final:被final修饰的方法将无法被重写,被final修饰的参数将无法被修改,即常量的意思。
final 常量名=值; final double PI=3.14;
变量命名规范 1.所有变量、方法、类名:见名知意 2.类成员变量:首字母小写和驼峰原则: monthSalary 3.局部变量:首字母小写和驼峰原则 4.常量:大写字母和下划线:MAx_VALUE 5.类名:首字母大写和驼峰原则: Man, GoodMan 6.方法名:首字母小写和驼峰原则: run(), runRun()
6.6 运算符
1、算术运算符:+,-,,l, %,++,-- 2、赋值运算符= 3、关系运算符:>,<,>=,<=,==,!= instanceof 4、逻辑运算符:&&,||,! 5、位运算符: &,|,^, ~,>>,<<,>>>(了解!!! ) 6、条件运算符 ? ︰ 7、扩展赋值运算符:+=,-=,*=,/= 8、cast:转化的意思
public class test{ public static void main(String[] args){ //两个整数相除,可能丢失精度 int a=9; int b=2; System.out.println(a/b);//4,输出一个整数 //byte+short byte c=8; short d=6; System.out.println(c+d);//返回int //自增 ++ 自减 -- int i=3; System.out.println(i++);//3 System.out.println(++i);//5 //与(and) 或(or) 非(!取反) boolean a1=true; boolean b1=false; System.out.println(a&&b);//false System.out.println(a||b); System.out.println(!(a&&b)); //短路验证 int c1=3; boolean d1=(c1<2)&&(c++<4);//false System.out.println(c1)//3 /* a=0011 1100 b=0000 1101 a&b=0000 1100 a|b=0011 1101 a^b=0011 0001 异或 ~b=1111 0010 <<左移 *2 >>右移 /2 */ int a=10; a+=10;//a=20; int b=10; System.out.println(" "+a+b); System.out.println(a+b+" "); } }
6.7 JavaDoc生成文档
javaDoc(文档注释)生成文档: @author 作者名 @version版本号 @since指明需要最早使用的jdk版本 @param参数名 return返回值情况 @throws异常抛出情况
package com.test.doc; /** * * @author MYA20000201 * @version * @since * */ public class Doc { String name; /** * @author MYA20000201 * @throws * */ public String test(String name) throws Exception{ return name; } }
1、进入该文件的目录 2、javadoc -encoding UTF-8 -charset UTF-8 文件.java 3、会生成文档
7、Java流程控制
7.1 用户交互Scanner
Scanner扫描器,用来接收键盘数据 System.in系统输入
-
next()
-
1、一定要读取到有效字符后才可以结束输入。
-
2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
-
3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
-
4、next() 不能得到带有空格的字符串。
-
-
nextLine()
-
1、以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。
-
2、可以获得空白。
-
//next的使用 public static void main(String[] args) { //创建Scanner对象,接受键盘输入的数据 System.out.println("请输入第一个字符串:"); Scanner in=new Scanner(System.in); //判断用户是否输入 if(in.hasNext()){ //输入就输出该值 System.out.println("next为:"+in.next()); } in.close(); } /* 请输入第一个字符串: hello word! next为:hello */ //nextLine的使用 public static void main(String[] args) { Scanner in= new Scanner(System.in); System.out.println("请输入:"); if(in.hasNextLine()){ String str=in.nextLine(); System.out.println("该值为:"+str); } in.close(); } /* 请输入: hello word! 该值为:hello word! */
Scanner其他方法 1.nextInt() 2.nextFloat() 3.hasNextInt()
7.2 顺序结构
语句与语句之间,框与框之间是按从上到下的顺序进行的,它是由若干个依次执行的处理步骤组成的,它是任何- -个算法都离不开的一种基本算法结构。
Java程序执行顺序结构
7.3 分支结构
-
if单选择结构:if(boolean表达式){如果条件为真就执行的语句}
-
if双选择结构:if(boolean表达式){如果条件为真就执行的语句}else{条件为假就执行该语句}
-
if多选择结构:if(boolean表达式){如果条件为真就执行的语句}else if(boolean表达式){如果条件为真就执行的语句}……else{条件为假就执行该语句}
-
嵌套的if结构:if(boolean表达式){ if(boolean表达式)……else{} }
-
switch多选择结构:switch(变量){case 值:break case 值:break default:}:根据变量的值与case后面的值相匹配,如果哪个case后面的值匹配上了,则执行该case下面所有语句块。注意:是所有的语句块,也就是说后面的case不会再进行匹配了,直到遇到break 才跳出switch语句。
public static void main(String[] args) { //if单选择结构 int i=1; if(i==1){ System.out.println(i==1); } //if双选择结构 if(i==2){ System.out.println(i==2); }else{ System.out.println(i==2); } //if多选择结构 if(i==3){ System.out.println(i==3); }else if(i==2){ System.out.println(i==2); }else{ System.out.println(i==1); } //嵌套的if结构 if(i==1){ if(i>0){ System.out.println(i>0); }else{ System.out.println(i<0); } } //switch多选择结构 switch(i){ case 0: System.out.println("0"); break; case 1: System.out.println("1"); break; default: System.out.println("其他"); } } /* true false true true 1 */
7.4 循环结构
-
while循环:while(条件判断){//循环内容 //结束循环的变量}
-
do……while循环:do{//循环内容 //结束循环的变量}while(条件判断);
-
for循环:for(变量初值;条件判断;迭代){//循环内容}
-
for的加强版(java5):for(Object obj:Objects){//循环内容}
注意:1、for语句变量初值,条件判断,迭代可以为空值 2、do……while不管条件是否符合都会执行一次
public static void main(String[] args) { int i=1; int sum=0; int sum1=0; int sum2=0; int[] number={1,2,3,4}; //while while(i<=100){ sum+=i; i++; } //do……while i=0; do{ sum1+=i; i++; }while(i<=100); //for for(i=0;i<=100;i++){ sum2+=i; } System.out.println("while遍历的1至100:"+sum); System.out.println("do……while遍历的1至100:"+sum1); System.out.println("for遍历的1至100:"+sum2); //for加强版 for(int j:number){ System.out.println("数组第"+number[j-1]+"为:"+j); } } /* while遍历的1至100:5050 do……while遍历的1至100:5050 for遍历的1至100:5050 数组第1为:1 数组第2为:2 数组第3为:3 数组第4为:4 */
7.5 break & continue & goto关键字
-
break在任何循环语句的主体部分,均可用break控制循环的流程,break用于强行退出循环. . 不执行循环中剩余的语句。(break语句也在switch语句中使用) break 后面可以追加循环标记
-
continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句, 接着进行下一次是否执行循环的判定。
public static void main(String[] args) { //break continue int sum=0,sum1=0; for(int i=0;i<=100;i++){ //输出i等于30 if(i==100){ System.out.println("i等于:"+i); break;//i=100之后,结束整个循环体 } if(i==50){ continue;//i=50之后,后面程序不会执行 } sum1+=i; } System.out.println("除50,100外1至100之内的和:"+sum1); } /* i等于:100 除50外100之内的和:4900 */
goto使用了解
public static void main(String[] args){ public static void main(String[] args) { //打印101到150的质数 int count=0; outer:for(int i=101;i<=150;i++){ for(int j=2;j<i/2;j++){ if(i%j==0){ continue outer; } } System.out.print(i+" "); } } } /* 101 103 107 109 113 127 131 137 139 149 */
8、Java方法和函数
8.1 方法及main方法
方法:方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用,提高代码的重用性、可维护性,减少代码冗余。
方法结构:
访问修饰符 [static] [final] 返回类型 方法名([final] 类型1 参数1, [final] 类型2 参数2, 类型n 参数n) { 这儿就是一组需要重复执行的代码块。 return 返回值; //可省略 }
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
Java主启动方法main
main()方法是java应用程序的入口方法,程序在运行的时候,第一个执行的方法就是main方法。 1. 访问修饰符必须是public 2. 必须是静态的(static) 3. 没有返回值,只能使用void 4. 具有一个字符串数组参数,用于接收执行java程序的命令行参数,命令行参数作为字符串,多个参数的话用空格隔开,并按照顺序依次对应字符串数组中的元素。 例如:#java TestMain hello world teacher hong 5. 一个类只能有一个main()方法
访问修饰符
有4种,可以为private、protected、public和默认不写,区别如下: 访问范围 private [默认] protected public 同一个类 可访问 可访问 可访问 可访问 同一个包中的其它类 不可访问 可访问 可访问 可访问 不同包中的子类 不可访问 不可访问 可访问 可访问 不同包中的非子类 不可访问 不可访问 不可访问 可访问
静态static
静态的意思,是可选的。如果方法加了static,就称为“静态方法”,反之称为“成员方法”。 静态方法属于类的,由类来调用。成员方法属于对象(实例),由对象(实例)来调用。
//static修饰的方法可以被其他调用,而static方法只能调用static方法 //main方法 public static void main(String[] args) { int sum=add(4,9); System.out.println("sum的值为:"+sum); } //加法 public static int add(int a,int b){ int sum=0; sum=a+b; return sum;//return 1.返回值2.结束方法 } //sum的值为:13
//外部类调用 public void compare(int a,int b){ System.out.println("最大值为:"+(a>b?a:b)); } //main public static void main(String[] args) { CompareTest ct=new CompareTest(); ct.compare(5, 3); } /* 最大值为:5 */
8.2 方法重载
-
方法名称必须相同。
-
参数列表必须不同(个数不同、或类型不同、参数排列顺序不同等)。
-
方法的返回类型可以相同也可以不相同。
-
仅仅返回类型不同不足以成为方法的重载。
public static void main(String[] args) { int sum=0; double sum1=0; sum=add(6,7); sum1=add(6.0,7.0); System.out.println("add(int a,int b)返回值为:"+sum); System.out.println("add(double a,double b)返回值为:"+sum1); } public static int add(int a,int b){ int sum; sum=a+b; return sum; } public static double add(double a,double b){ double sum; sum=a+b; return sum; } /* add(int a,int b)返回值为:13 add(double a,double b)返回值为:13.0 */
8.3 可变参数
-
JDK 1.5开始,Java支持传递同类型的可变参数给一个方法
-
在方法声明中,在指定参数类型后加个省略号(...)
-
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。
在方法的声明中,在指定参数类型后面加3个点的省略号(...),语法结构: static 返回类型 方法名(参数类型... 参数名){ 代码块。。 }
public static void main(String[] args) { show(12,23,34,54); } //可变参数会被编译器转型为一个数组 public static void show(int... i){ for(int j=0;j<i.length;j++){ System.out.print(i[j]+" "); } } /* 12 23 34 54 */
8.4 递归算法
递归就是在方法体内部调用方法本身,产生的现象叫递归。自己调用自己,就是简单的递归。谨慎使用!!!
//递归计算阶乘 public static void main(String[] args) { Scanner in=new Scanner(System.in); int i=0; System.out.println("请输入一个数:"); int j=in.nextInt(); i=f(j); System.out.println(j+"的阶乘为:"+i); in.close(); } public static int f(int i){ if(i==1){ return 1; }else{ return i*f(i-1); } }
9、Java面向对象
面向对象最关键是的两个概念就是类和对象,类是对象的抽象(类别),对象就是类的实例(具体的对象)。 在一个类中定义对象所具有的属性和方法,面向对象对简称OO(Object Oriented),面向对象程序设计称为OOP。 对象Java世界来说,一切皆对象。把现实世界中的对象抽象地体现在编程世界中,一个对象也代表某个具体的操作。
9.1 面向对象与面向对象
面向过程:一步一步做 面向对象:分类思想
9.2 什么是面向对象
-
面向对象编程(Object-Oriented Programming,OOP)
-
面向对象编程的本质是:一类的方式组织代码,一对象的方式封装数据
-
抽象
-
三大特征:封装,继承,多态
9.3 类 & 对象及创建对象的概念
类实际上就是表示一个客观世界中某个群体的一些基本特征的抽象。 对象就是真实世界中的实体,对象与实体是一一对应的,也就是说现实世界中每一个实体都是一个对象,它是种具体的概念。
类是抽象数据类型,是某一类事物整体的描述,不代表具体的事物。对象是抽象的具体实例
1. 显式创建 (1)类 对象变量 = new 类(); (2)通过Class对象的newInstance(); 这种方法等同于new 类();而且执行的是无参构造方法; (3)通过clone()方法进行深拷贝; 通过 = 直接赋值的新变量,与原变量在内存中实际指向的是同一个数据,这种方式称为浅拷贝。 通过clone()方法赋值的新变量,与原变量没有任何关系,是根据原变量复制的一份新的变量,这种方式称为深拷贝。 2. 隐含创建 String strName = "strValue"; 其中的“strValue”就是一个String对象,由Java虚拟机隐含地创建的。
public class Person{ //属性 String name; int id; int age; //方法 public void show(){ System.out.println("name:"+name); } } public static void main(String[] args){ //new关键字创建对象以外,还会默认初始化对象 Person person=new Person(); //类的实例化 person.name="张三"; person.age=18; person.id=2020233; person.show(); }
9.4 构造方法
1、必须与类名相同 2、必须没有返回值 3、一个类即时没有什么可写入的代码,它都还有一个方法,构造方法
构造方法是类的一种特殊方法,用于实例初始化工作的,在创建对象(new 类)之后自动调用的。 Java中每一个类都有一个默认的构造方法,并且可以有多个构造方法。 构造方法的特点: 1. 方法名与类名必须相同; 2. 可以有0个或者多个参数; 3. 没有任何返回值,包括void; 4. 构造方法可以有多个,并形成重载; 5. 只能与new 操作结合使用,不能单独调用; /** * 无参构造方法 */ public Person(){ } /** * 有3个参数的构造方法 * @param name * @param sex * @param age */ public Person(String name, boolean sex, short age) { setName(name); setSex(sex); setAge(age); }
建议:使用有参构造方法,最好定义好无参构造方法
9.5 析构方法
析构方法与构造方法刚好相反,当对象被销毁的时候,系统会自动执行该对象的析构方法。 通常用于做一些清理垃圾碎片的工作。 @Override protected void finalize() throws Throwable { //清理工作的代码.. super.finalize(); }
9.6 封装
封装就是类的某些属性隐藏在类的内部,不允许外部程序直接访问并操作属性, 只能通过该类提供的如setXX方法来对隐藏属性的操作,从而起到保护属性的作用。 操作步骤: 1. 将属性的访问修饰符设置为 private 2. 提供如setXX的方法并设置为 public
1、封装数据隐藏,高内聚,低耦合 2、属性私有get/set 3、私有关键字private
//Stusdent.java public class Student { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { if(age>0&&age<140){ this.age = age; System.out.println("你的年龄为"+age); }else{ //如果年龄输入不符合实际,就赋值为18 this.age=18; System.out.println("如果年龄输入不符合实际,就赋值为18"); } } } //Packing.java public static void main(String[] args) { Student student=new Student(); student.setAge(18); } /* 你的年龄为18 */
9.7 继承
继承是面向对象的三大特征之一,继续和现实生活中的继承的相似之处就是保留一些父辈的特征,从而减少代码的冗余,提高程序的重用性、可维护性、程序的执行效率。 Java中的继承就是在已经存在的类的基础上进行扩展,从而产生新的类。已经存在的类称为父类、基类或超类,而新产生的类称为子类或者派生类。 在子类中,不仅可以包含父类的属性和方法,还可以增加新的属性和方法; 继承的语法格式: public class 子类名 extends 父类名{ } 注意:Java中的继承不支持多继承,只允许单继承,一个子类只能有一个父类。
1、继承的本质就是对某一批类的抽象
2、extends的意思就是“扩展”,自雷是父类的扩展
3、子类为派生类,父类为基类
4、java中只有单继承,没有多继承
5、不能继承私有属性、及final关键字修饰的方法
6、@Override (告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。)方法覆盖
final关键字再现
如果final加在类的前面,如:public final class 类{},这个类就被定义为“最终类”或者“太监类”,这样类是无法被别的类继承的。 为什么要把某些类定义为最终类呢? 1. 为了安全,不允许被其它类继承,也就是没有子类,那么这个类的所在属性、方法都是绝对安全的。 2. 执行速度要快一些,由于它的方法无法再被重写(覆盖),所有其地址引用和装载在编译期间完成,而不是在运行期间由JVM进行的装载,因为更简单有效。
public class Person{ private String name; int age; public void speaking(){ System.out.println("说了一句话!") } } public class Student extends Person{ } public static void main(String[] args) { Student student=new Student(); student.age=18; student.speak(); } /* 说了一句话! */
9.8 多态(Polymorphism)
多态是Java面向对象的第三个特征,它是指在父类中定义的属性和方法被子类继承后,可以具有不同的行为并表现出不同的形态,这就是所谓的“多态”; 要实现多态,有三个必要条件: 1. 继承,在多态中必须存在有继承关系的子类和父类; 2. 向上转型,在多态中需要将子类引用赋值给父类对象,只有这样该引用才能调用子类方法;处理不当会报ClassCastException 异常 3. 方法重写,子类要对父类的某些方法进行重写,在调用这些方法的时候,就会调用子类的方法;
-
继承(类和类)或实现(接口和实现类)
class Animal { } class Dog extends Animal { } class Cat extends Animal { } Animal a = null; a = new Dog(); a = new Cat();
-
重写方法
在子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体的实现不同,以实现不同于父类的功能, 这种方式称为方法的重写(override),也称为方法覆盖。
public class SuperClass { String name = "父类的实例变量"; public void superMethod() { System.out.println("父类中普通的实例方法"); } protected void test() { System.out.println("父类中被覆盖的实例方法"); } } public class SubClass extends SuperClass { String name = "子类的实例变量"; public void subMethod() { System.out.println("子类中普通的实例方法"); } @Override public void test() { System.out.println("子类中覆盖父类的实例方法"); } public static void main(String[] args) { SuperClass p = new SubClass(); System.out.println(p.name); // 父类的实例变量 System.out.println(((SubClass)p).name); // 子类的实例变量 p.superMethod(); // 父类中普通的实例方法 p.test(); // 子类中覆盖父类的实例方法 // p.subMethod(); // 编译报错,SuperClass 中没有 subMethod() ((SubClass)p).subMethod(); // 子类中普通的实例方法 } }
-
向上转型
将一个类型强制转换为另一个类型的过程就叫类型转换。 1. 向上转型 父类引用指向子类对象的过程称为向上转型,语法格式: 父类 obj = 子类对象; 2. 向下转型 与向上转型相反,子类对象指向父类引用的过程称为向下转型,语法格式: 子类 obj = (子类)父类对象; 注意:只有父类对象引用指向的对象类型与要转换的目标类型一致才能转换成功,否则就会报错。
// 向上转型:把子类对象赋给父类引用变量(多态) Animal a = new Dog(); Object obj = new Dog(); // 强制类型转换:把父类对象赋给子类引用变量 Dog d = (Dog) a; Cat c1 = (Cat) a; // ClassCastException: Dog cannot be cast to Cat Cat c2 = (Cat) new Animal(); // ClassCastException // 在进行强制类型转换之前,先用 instanceof 运算符判断是否可以成功转换,从而避免出现 ClassCastException 异常 if (a instanceof Dog) { Dog d = (Dog) a; }
9.9 this & super & null & final关键字
-
this关键字
1、this在是类里面指的是当前的实例对象,通过this调用当前实例对象的任何属性以及任何方法。 2、this.属性名 3、this.方法() 4、通常情况下,this关键字我们是可以省略的。
-
super关键字
1、由于子类不能继承父类的构造方法,因此,如果要调用父类的构造方法就需要使用super关键字。 2、super可以用来访问父类的构造方法、普通方法和属性。
-
null关键字
1、表示对象为空的意思,比如只是声明了一个对象,但是未对对象进行赋值,这时候该对象的值就是null。 2、注意:当对象为null的时候,不能去调用对象的属性或者方法,否则会报空指针异常。
-
final关键字
1、修饰变量的时候一旦修饰就不能进行修改 2、修饰类防止继承的类对该类进行修改,该类当中的方法都会隐式的指向为final方法。 3、修饰方法 防止方法被修改 提高效率
class Person { int id; String name; Person(int id, String name) { this.id = id; this.name = name; } } class Emp extends Person { float salary; Emp(int id, String name, float salary) { super(id, name); this.salary = salary; } void display() { System.out.println(id + " " + name + " " + salary); } } public class CallParentParamConstrutor { public static void main(String[] args) { new Emp(1, "沉默王二", 20000f).display(); } }
9.10 判断对象的类型:instanceof 关键字
判断对象是否符合指定的类型,结果要么是 true,要么是 false。在反序列化的时候,instanceof 操作符还是蛮常用的,因为这时候我们不太确定对象属不属于指定的类型,如果不进行判断的话,就容易抛出ClassCastException 异常。 instanceof 操作符正是基于类与类之间的继承关系,以及类与接口之间的实现关系的。
instanceof对于null值是否报错?
只有对象才会有 null 值,所以编译器是不会报错的,只不过,对于 null 来说,instanceof 的结果为 false。因为所有的对象都可以为 null,所以也不好确定 null 到底属于哪一个类。
// 先判断类型 if (obj instanceof String) { // 然后强制转换 String s = (String) obj; // 然后才能使用 } //先用 instanceof 进行类型判断,然后再把 obj 强制转换成我们期望的类型再进行使用。 //JDK 16 的时候,instanceof 模式匹配转了正,意味着使用 instanceof 的时候更便捷了。 if (obj instanceof String s) { // 如果类型匹配 直接使用 s } Object obj = "ABC"; obj instanceof String; // ture obj instanceof Object; // ture obj instanceof Math; // false String str = "ABC"; str instanceof Math; // String 类与 Math 类没有继承关系,编译报错 // 获取当前对象的运行时类型 obj.getClass(); // class java.lang.String obj.getClass() == String.class; // ture obj.getClass() == Object.class; // flase
10、Java抽象类(接口)及其他类
-
抽象方法
1、使用 abstract
修饰的且没有方法体的方法 (没有方法体与空方法体不同) 2、不能使用 private
、final
或 static
修饰 3、只能定义在抽象类或接口中 4、子类继承父类时,必须重写改抽象方法
10.1 抽象类
1、使用abstract修饰 2、有构造器,但不能直接new来创建对象,只留给子类创建对象时调用 3、抽象类中可以包含普通方法 4、抽象类继承,子类要重写抽象方法
public abstract class Person{ public abstract void show(); }
public class Student extends Person{ @Override public void show() { System.out.println("我是一个学生!"); } }
10.2 接口
1、接口定义一种规范,规定某一批类里必须提供某些方法,要求这些类必须完全实现接口里所定义的全部抽象方法,从而实现接口中定义的功能。 2、接口是对功能的抽象
-
接口成员特点
1、没有构造器,不能实例化 2、成员变量默认都使用public static final修饰,全局静态常量 3、抽象方法默认都使用public abstract修饰,公共的抽象方法,所以实现类中的实现方法必须使用public修饰(接口里定义的方法一般不写修饰符) 4、内部类默认都使用public static修饰,公共的静态内部类接口中的默认方法不需要实现类实现
-
关系
1、接口与接口之间继承关系extends,支持多继承 2、类与接口之间有实现关系implements ,可以多实现 一个类实现了一个或多个接口之后,这个类必须完全实现(重写)这些接口里所定义的全部抽象方法,否则该类必须定义成抽象类 实现接口方法时,必须使用public修饰
//Animal接口 public interface Animal { public void run(); } //接口继承 public interface Person extends Animal{ public void useTools(); } //接口实现 public class Student implements Person{ @Override public void useTools() { System.out.println("人类会使用工具!"); } @Override public void run() { System.out.println("动物都会running"); } }
10.3 基本类型包装类
-
装箱和拆箱
1、装箱:把基本类型数据转成对应的包装类对象 2、拆箱:把包装类对象转成对应的基本数据类型数据 自动装箱:把一个基本类型变量直接赋给对应的包装类变量或Object变量。在底层依然是手动装箱,使用的是 xxx.valueof()方法。因此,int i= 17; object[ ] arr = {"A",12,3.14, true}; 自动拆箱:把包装类对象直接赋给对应的基本数据类型变量,或者包装类对象与基本数据类型变量使用“=="比较。在底层依然是手动拆箱,使用的是xxxvalue()方法。因此, switch 语句也支持 byte, short, char, int对应的包装类
基本数据类型 包装类 bype Byte short Short int Integer long Long float Float double Double char Character boolean Boolean 基本数据类型转换为包装类的过程称为装箱,当将基本数据类型直接赋值给包装类引用对象时,基本数据变量会自动装箱为包装类引用对象。 方法:1.包装类 对象 = 基本数据类型变量; 如:Integer a = 10; 2.包装类 对象 = new 包装类(基本数据类型); 如:Integer b = new Integer(10); 包装类转换为基本数据类型的过程称为拆箱,当包装类需要参与数学运算的时候,包装类引用对象也会自动拆箱为基本数据类型。 通过调用包装类对象的.intValue()、.byteValue()、.longValue()、.doubleValue()
-
包装类的需要及意义
1. 包装类提供了很多将字符串转换为基本数据类型的方法; 2. 我们就可以充分使用面向对象的方式来操作这些数据变量,比如继承、多态、向上转型;
-
包装类常见方法
1、包装类中 public static final
修饰的字段:MIN_VALUE
、MAX_VALUE
、SIZE
(在内存中存储占的比特位数)、TYPE
(对应基本类型的 Class 对象)
2、包装类的构造器,用于创建对应的包装类对象(xxx 类型的包装类 Xxx)
-
Xxx(xxx value):a. 装箱,如
Integer(int value)
Boolean(boolean value)
-
Xxx(String str):f. 字符串 → 包装类
-
Character 类没有此构造器
-
Boolean(String str)
// 如果 str 不为 null 且在忽略大小写时等于 "true",则创建一个表示 true 值的 Boolean 对象,否则创建一个表示 false 值的 Boolean 对象 -
当该字符串不能转换为适当格式时,抛出异常 NumberFormatException
-
3、Short、Integer、Long、Float 和 Double 类中常用的静态方法
-
xxx sum(xxx a, xxx b)
:将两个数相加 -
xxx min(xxx a, xxx b)
:返回两个数的较小值,Math.min
-
xxx max(xxx a, xxx b)
:返回两个数的较大值,Math.max
-
int compare(xxx a, xxx b)
:比较两个数值,a 小于、等于或大于 b 则分别返回 -1、0 或 1 -
xxx getXxx(String nm, Xxx val)
:获取指定名称的系统属性值,如果没有则返回 val
//基本数据与字符串转化 int hh = 123; String s1 = hh + ""; String s2 = String.valueOf(hh); String s3 = Integer.toString(hh); String str = "123"; int str1 = Integer.parseInt(str); int str2 = Integer.valueOf(str);
4、其他
1、包装类与基本类型的默认值
包装类默认NULL | 数据类型 | 字节数 | 位数 | 包装类 | 默认值 |
---|---|---|---|---|---|
整型 | byte/short/int/long | 1/2/4/8 | 8/16/32/64 | Byte/Short/Integer/Long | 0/0/0/0L |
浮点型 | float/double | 4/8 | 32/64 | Float/Double | 0.0f/0.0d |
布尔型 | boolean | 1 | 8 | Boolean | false |
字符型 | char | 2 | 16 | Character | '\u000' |
2、数组length与String的length() 3、超过数组长度,报ArrayIndexOutOfBoundsException 4、数组部分未定义,也没有默认值,报NullPointerException
10.4 匿名内部类
-
内部类:定义在类结构中的另一个类,编译后,每个内部类都会生成对应的 .class 文件
在类的内部可以定义成员变量和方法,而且在类的内部也可以定义另一个类。如果在外部类的内部再定义一个类,此时内部类也称嵌套类。 内部可以很好的实现隐藏在外部类里面,一般的非内部类不允许private与protected权限,但是内部类却是可以。 内部类可以分为:成员内部类、静态内部类、局部内部类,每种内部类都它的特定一些特征。 1. 成员内部类,无法在外部直接实例,必须要先实例所在外部类,再通过对象实例内部类; 2. 静态内部类,与对象无关,属于类级别,所以在外部可以直接实例; 3. 局部内部类,是创建在方法里面的类,该类的访问权限仅限于该方法中;
-
匿名类
匿名类是指没有类名的局部内部类,必须在创建时使用new 语句来声明类,语法格式: new 类或者接口(){ //匿名类的主体 } 特点:匿名类和局部内部类一样,都可以访问外部类所有成员。
// public interface Inr1 { public void show(); } public class Test1 { public static void main(String[] args) { //匿名内部类不能定义构造器,不能是抽象类 new Inr1(){ @Override public void show() { System.out.println("hello world !"); } }.show(); } }
-
Object类
Object类是所有类的父类,也叫超级基类,换句话说Java的所有类都继承了Object类,所以我们所以的Java类都可以使用Object类的方法。 常用方法: String toString(); 返回对象的字符串表示形式,默认:类命名@字符型hashCode boolean equals(Object obj); 比较两个对象是否相等,默认:比较两个变量的内存地址是否相等,相等于== int hashCode(); 获取对象的hash值 Object clone(); 创建并返回呈个对象的拷贝,(深拷贝) void finalize(); 析构方法,当GC确定不存在对该对象的引用时,由对象的垃圾回收器调用此方法 Class<?> getClass(); 获取Class类的方法
11、Java字符串
字符串是Java中的一个特殊类,使用方法和包装类非常相似。定义方法有两种: 1. String 变量 = "Hello java"; 【推荐写法】 2. String 变量 = new String("Hello java"); 其中字符串赋的值是不可变的,这里这个”不可变“指的是存放于JVM常量区里面的字符串数据。
11.1 常见方法
1、字符串拼接,使用"+"或者字符串1.concat(字符串2) 2、字符串长度,字符串.length(),注意:一个空格、一个汉字、一个符号都算1个单位长度 3、字符串大小转化,大转小:字符串.toLowerCase();大转小:字符串.toLowerCase();注意:这俩方法只对字母有效,对汉字、符号无效果的。 4、去掉首位空格,字符串.tirm() 5、注意:String重写了equals().hashCode()方法
字符串比较
字符串.equals(String 目标字符串); 注意:该方法是比较两个字符串的值是否相等,根据两个字符串是否为同一内存数据地址无关,这里是重写了Object类的equals方法。 和 == 的区别? 答:== 是比较两个对象的内存地址是否一样,equals是Object超级基类的方法,默认和 == 完全一样,但String类重写了equals方法, 使得equals方法变成了比较字符串内容是否一样,不再是比较内存地址了。 字符串.equalsIgnoreCase(String 目标字符串); 和equals()方法相比,区别就是该equalsIgnoreCase方法会忽略大小写比较。
6、字符串截取,字符串.substring(int beginIndex, int endIndex); beginIndex初始下标,endIndex()结束下标(不包含) 7、字符串查找,三种情况:
字符串查找
1. 字符串.indexOf(String 查找源, int fromIndex); 从字符串自左向向进行查询,返回的数据是参数“查询源”首次出现的下标位置,如果未查到,则返回-1; fromIndex参数表示从哪个下标开始查找【可省略】,省略后相当于0 2. 字符串.lastIndexOf(String 查找源, int fromIndex); 从字符串自左向向进行查询,返回的数据是参数“查询源”最后一次出现的下标位置,如果未查到,则返回-1; fromIndex参数表示从哪个下标开始查找【可省略】,省略后相当于字符串长度 3. 字符串.charAt(int 下标); 通过下标位置返回对应的字符
8、字符串替换
字符串替换
字符串.replace(String oldStr, String newStr); 参数说明: oldStr表示需要被替换的源字符/串 newStr表示被替换之后的字符/串 字符串.replaceAll(String regex, String newStr); 参数说明: regex表示需要被替换的源字符/串匹配的正则表达式 newStr同replace()方法 字符串.replaceFirst(String regex, String newStr); 参数说明: regex表示需要被替换的源字符/串匹配的正则表达式 newStr同replace()方法 三种方法的区别: replace只能提供精确查找并替换 replaceAll和replaceFirst可以根据正则表达式来匹配查找并替换 其中replaceAll是全部替换,replaceFirst只替换第一个匹配的字符/串
9、字符串以某个字符/串开始或者结束:字符串.startWith(String str)/字符串.endWidth(String str) 10、拆分数组/转化成数组:字符串.split('分隔符')/字符串.getBytes()
11.2 String类
String.valueOf(变量); 将别的类型转换为字符串 String.join('分隔符',数组或集合);
11.3 StringBuffer & StringBuilder
StringBuffer和StringBuilder都可以创建字符序列可变的字符串对象,功能方法相同的,都是最终类,但value没有使用final修饰 StringBuffer类使用了synchronized修饰符,线程安全; StringBuilder类没有使用synchronized修饰符,性能更高,但线程不安全(建议使用StringBuilder) 区别: 线程安全: StringBuffer的线程是安全的 StringBuilder的线程是不安全的 执行效率: StringBuffer较慢一点,但也比String拼接快很多 StringBuilder较快一点
-
StringBuilder为例对象创建
1、new StringBuilder():创建一个不带字符,初始容量为 16 个字符的字符串缓冲区 2、new StringBuilder(int capacity):创建一个不带字符,指定初始容量的字符串缓冲区 3、new StringBuilder(String str):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容
-
StringBuilder()放用方法
1、append(Object o):追加 2、deleteCharAt(int index):删除 3、insert(int offset,Object o):插入 4、reverse():反转 5、void setLength(int newLength):设置字符序列的长度(如果 newLength 参数小于当前长度,则长度将更改为指定的长度,如果 newLength 参数大于或等于当前长度,则将追加有效的 null 字符,使长度满足 newLength 参数)
11.4 String\StringBuffer\StringBuilder使用场景
1.操作少量的字符串拼接、修改、反转等操作时可以使用String; 2.操作大量的字符串接、修改、反转等操作时可以使用StringBuffer、StringBuilder, a.如果单线程里面推荐使用StringBuilder b.如果多线程里面推荐使用StringBuffer
12、Java数学类
在Java中对于简单的数据运算我们可以使用+-*/和%来进行计算,但是对于比较复杂的数学运行,比如三角函数、对数函数等, Java提供了一个Math类来完成这些复杂的数学运算。
12.1 常见方法(随机、绝对、三角函数)
1、Math类中的静态变量,字段:E、PI,Math.E/Math.PI 2、double random():返回带正号的 double 值(伪随机数),[0.0, 1.0) 3、int min(int a, int b):返回两个数的较小值 4、int max(int a, int b):返回两个数的较大值 5、int toIntExact(long value):将 long 值转换为 int 值 6、long round(double a)、int round(float a):四舍五入,根据小数点后的第一们小数来进行 7、double ceil(double a): 向上取整,即返回大于或等于参数的最小整数; 8、double floor(double a):向下取整,即返回小于或等于参数的最大整数; 9、rint(数字); 返回最接近参数的整数值,如果有两个同样接近的整数,则结果取偶数; 10、Math.abs(数字);绝对值
-
Math指数运算
Math.pow(double a, double b); 返回以a为底数,b为指数的幂值 Math.exp(double a); 返回以e为底数,a为指数的幂值 Math.sqrt(double a); 返回a的平方根 Math.cbrt(double a); 返回a的立方根
-
Math三角函数
Math.sin(double a); 返回角的正弦值,参数为弧度单位 Math.cos(double a); 返回角的余弦值,参数为弧度单位 Math.tan(double a); 返回角的正切值,参数为弧度单位 Math.asin(double a); 返回角的反正弦值,参数为弧度单位 Math.acos(double a); 返回角的反余弦值,参数为弧度单位 Math.atan(double a); 返回角的反正切值,参数为弧度单位 Math.toRadians(double a); 把角度换算为弧度 Math.toDegrees(dboule a); 把弧度换算为角度 弧度与角度换算公式: 弧度 = 角度 * (Math.PI / 180);
-
生成随机数
主要使用Random类来生成我们需要的随机数。 Random random = new Random(); random.nextInt(int a); 随机生成0-a之间的整数,注意不包含a本身的; random.nextDouble(); 随机生成0-1之间的浮点数,注意不包含1本身; random.nextFloat(); random.nextLong(); random.nextBoolean(); 在true和false之间随机二选一
12.2 BigDecimal
-
BigDecimal 不可变的、任意精度的有符号十进制数,用于处理金钱和精度要求高的数据
用于高精度计算,主要使用的类BigDecimal这个类 BigDecimal bd1 = new BigDecimal("数字"); bd1.add(BigDecimal bd2); 加法 bd1.subtract(BigDecimal bd2); 减法 bd1.multiply(BigDecimal bd2); 乘法 bd1.divide(BigDecimal bd2, int scale, int 截取类型); 除法 bd1.compareTo(BigDecimal bd2); 用于比较两个数的大小 如果bd1大于bd2,则返回1 如果bd1小于bd2,则返回-1 如果bd1等于bd2,则返回0
1、BigDecimal除法:BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
:返回一个 BigDecimal,其值为 (this / divisor),要保留的小数位数 scale,舍入模式 roundingMode(如 RoundingMode.HALF_UP 表示四舍五入) 2、BigDecimal setScale(int newScale, RoundingMode roundingMode)
, BigDecimal setScale(int newScale, int roundingMode)
:返回一个 BigDecimal,要保留的小数位数 newScale,舍入模式 roundingMode
12.3 数字格式化
主要使用DecimalFormat类来进行数字格式仳处理,比如将小数位统一成2位,不足2位的以0被全。 DecimalFormat format = new DecimalFormat("0.00");
// double 准确转换 BigDecimal BigDecimal dividend1 = new BigDecimal(Double.toString(0.9)); BigDecimal dividend2 = BigDecimal.valueOf(0.9); // BigDecimal dividend2 = BigDecimal.valueOf(0.9f); // 不能准确转换 // 注意:val 不能为 float,否则 float 转 double 时会丢失精度 public static BigDecimal valueOf(double val) { return new BigDecimal(Double.toString(val)); } // float 无法准确转换 BigDecimal // 比较大小 dividend1.compareTo(dividend2); // 结果为 0 时表示相等 // String 准确转换 BigDecimal,可能会抛出 NumberFormatException BigDecimal divisor = new BigDecimal("0.7"); // 如果无法表示准确的商值,则抛出 ArithmeticException // BigDecimal result = dividend1.divide(divisor); BigDecimal result = dividend1.divide(divisor, 2, RoundingMode.HALF_UP); // 1.29
13、Java日期与时间类
Java中主要使用Date类和Calendar类来处理日期与时间
13.1 Date类
-
构造器:Date()、Date(long date) Date date = new Date(); 获取当前日期与时间 Date date = new Date(long 毫秒数); 【推荐使用】
-
类方法:Date.from(Instant instant):从 Instant 对象获取一个 Date 的实例
-
Date实例常用方法: 1、date.getTime(); 获取对应的时间戳,自1970年1月1日00:00:00 GMT 以来此对象表示的毫秒数 2、string toLocalestring()∶返回此日期的字符串表示形式(使用语言环境约定)(已过时) 3、boolean equals(object obj):比较两个日期的相等性 4、boolean after(Date when) :判断此日期是否在指定日期之后 5、boolean before(Date when) :判断此日期是否在指定日期之前
13.2 Calendar类
-
类方法:Calendar getInstance()
日历类,这个类是抽象类,提供一些针对日期和时间的方法; 使用方法: Calendar calendar = Calendar.getInstance();
-
Calendar实例常用方法:
Calendar实例常用方法
calendar.setTime(Date date); 设置当前日期 calendar.getTime(); 获取当时日期,返回Date对象 calendar.add(int field, int amount) 根据日历的规则,为指定的日历字段(field)增加或者减少指定的时间量(amount) calendar.get(int field); 根据日历的规则,返回对应的字段值 calendar.compareTo(Calendar calendar2); 比较两个日历对象的大小,比较方式以及返回值和BigDecimal相似。 calendar.after(Object when); 判断此caleadar表示的时间是否在指定时间when的后面,并返回判断结果; calendar.before(Object when); 判断此caleadar表示的时间是否在指定时间when的前面,并返回判断结果;
13.3 LocalDate类、LocalDateTime
-
jdk1.8 的应用可以使用 LocalDateTime(代表不带时区的日期、时间)代替 Calendar
LocalDate类简述
创建: LocalDate date = LocalDate.now(); 获取当前时间 LocalDate date = LocalDate.of(2022, 6, 20); 通过参数指定年、月、日 LocalDate date = LocalDate.parse("2022-05-12"); 通过字符串参数来指定,注意月和日必须为两位数; 方法: date.getYear(); 获取年份 date.getMonthValue(); 获取月份 date.getDayOfMonth(); 获取日 date.format(DateTimeFormatter dateTimeFormatter); 参数说明:DateTimeFormatter是一个日期格式化类 创建方法:DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDateTime类简述
用法和LocalDate相似,只是增加了时、分、秒部分。 创建: LocalDateTime dateTime = LocalDateTime.now(); LocalDateTime dateTime = LocalDateTime.of(2022, 7, 12, 10, 23, 45); LocalDateTime dateTime = LocalDateTime.parse("2022-05-12T09:34:21"); 方法: LocalDate所有方法 dateTime.getHour(); 获取时 dateTime.getMinute(); 获取分 dateTime.getSecond(); 获取秒 dateTime.toEpochSecond(ZoneOffset.of("+8")); 获取距离1970-1-1 0:0:0的总秒数 dateTime.toInstant(ZoneOffset.of("+8")).toEpochMilli(); 获取距离1970-1-1 0:0:0的总毫秒数
13.4 日期格式化DateFormat、SimpleDateFormat
-
格式化(format)和解析(parse)日期或时间
-
jdk 1.8 的应用可以使用 DateTimeFormatter(时间格式器)代替 DateFormat、SimpleDateFormat
14、Java数组
14.1 数组简介
-
什么是数组?按一定顺序排列的同类型数据的集合称为数组,本身是一种引用类型,可以通过下标获取对应值,数组根据维度可分为一维数组、二维数组、多维数组
-
数组特征:一致性(存储数据类型)、有序性(下标存储及查找)、不可变性(长度)
14.2 数组声明、初始化及赋值
-
声明 1、数据类型 变量名[],如int arr[]; 2、数据类型[] 变量名,如int[] arr;
-
初始化 1、声明后,初始化;变量名=new 数据类型[长度];如arr=new int[6]; 2、声明加初始化,数据类型 变量名[]=new 数据类型[6];如int arr[]=new int[6];
-
赋值 1、变量[index]=值;如arr[0]=8; 2、int arr[]={1,2,4};
-
其他 1、操作数组常见异常,NullPointerException:空指针异常;ArrayIndexOutOfBoundsException:数组的索引越界异常 2、数组长度属性length
14.3 数组遍历
-
fori
for (int i = 0; i < 数组.length; i++){ 数组[i] //就是取值 }
-
foreach
for (数据类型 变量 : 数组) { 变量 //就是取值 }
14.4 二维数组及多维数组
二维数组
二维数组就是用两个下标来访问并操作元素,第一个下标可以理解为元素所在行(纵坐标),第二个元素可以理解为元素所在列(横坐标)。 数据类型[][] 变量名 = new 数据类型[size1][size2]; 如:Integer[][] numbers = new Integer[10][20]; size1 表示有多少行 size2 表示有多少列
多维数组
就是维度更高的数组,比如三维、四维。 数据类型[][][] 变量名 = new 数据类型[size1][size2][size3];
14.5 数组排序算法
14.5.1 冒泡排序
冒泡排序
是常用的数组、集合排序算法之一。 它的基本思想是:对比相邻的元素值,如果满足条件就交换元素值,把较少的元素值移动到前面。 把较大的元素移动到后面,也就是交换两个元素的位置,这样数组元素就像气泡一样从底部上长到顶部。 虽然说冒泡排序的算法比较简单,排序的结果也稳定,但是效率不高。
//冒泡 public static void maoPao(Integer[] arr){ int temp; for (int i = 0; i < arr.length-1; i++) {//每取出最大值 for (int j = 0; j <arr.length-i-1 ; j++) {//两两相比 if(arr[j]>arr[j+1]){ temp=arr[j+1]; arr[j+1]=arr[j]; arr[j]=temp; } } } for (Integer integer : arr) { System.out.print(integer+" "); } System.out.println(); }
14.5.2 快速排序
快速排序
快速排序是对冒泡排序的一种改进,是一种排序执行效率非常高的排序算法。 它的基本思想是:通过一趟排序,将要反序的数据分隔成独立的两部分,其中一部分的所有数据比另外一部分的所有数据都要小, 然后再按此方法对两部分数据进行快速排序,整个排序过程采用递归算法进行,以此使整个为成有序数量。
//快速 public static void kuaiSu(Integer[] arr,int start,int end){ if(start<end){ int temp; //设置一个数组中的元素为标准,这里选择第一个 Integer integer = arr[start]; //下面找integer的位置,思路从两边往中间查找 int left=start; int right=end; while (left<right){ //从右边找一个小于标准元素的值 while (left<right && integer<=arr[right]){ right--; } //从左边找一个大于标准元素的值 while (right>left && integer>=arr[left]){ left++; } //两个元素找到后,相互交换 if(left<right){ temp=arr[left]; arr[left]=arr[right]; arr[right]=temp; } } //位置找到后,就是位置元素与最初元素互换 arr[start]=arr[left]; arr[left]=integer; //递归左边 kuaiSu(arr,start,right-1); //递归右边 kuaiSu(arr,right+1,end); } }
14.5.3 Comparetor && Compareable接口
//lambda 按照编号来排序 Collections.sort(quoteType,(Comparator<Commodity>)(c1,c2)->{ return c1.getId()>c2.getId()?1:-1; }); for (Commodity commodity : quoteType) { System.out.println(commodity); } //匿名类 Comparator comparator=new Comparator() { @Override public int compare(Object o1, Object o2) { Commodity c1=(Commodity)o1; Commodity c2=(Commodity)o2; return c1.getPrice()>c2.getPrice()?1:-1; } }; Collections.sort(quoteType,comparator);public class Commodity implements Comparable{ private int id;//编号 private String name;//商品名 private float price;//价格 public Commodity(int id, String name, float price) { this.id = id; this.name = name; this.price = price; } @Override public int compareTo(Object o) { Commodity c=(Commodity)o; /* 0:表示相等 1:表示大于 -1:表示小于 */ return getPrice()>c.getPrice()?1:-1; } }
14.6 数组工具类Arrays
Arrays 类是一个工具类,其中包含了数组操作的很多方法。这个 Arrays 类里均为 static 修饰的方法(static 修饰的方法可以直接通过类名调用),可以直接通过 Arrays.xxx(xxx) 的形式调用方法。
Arrays工具类
1. int binarySearch(type[] a, type key) 使用二分法查询 key 元素值在 a 数组中出现的索引,如果 a 数组不包含 key 元素值,则返回负数。调用该方法时要求数组中元素己经按升序排列,这样才能得到正确结果。 2. int binarySearch(type[] a, int fromIndex, int toIndex, type key) 这个方法与前一个方法类似,但它只搜索 a 数组中 fromIndex 到 toIndex 索引的元素。调用该方法时要求数组中元素己经按升序排列,这样才能得到正确结果。 3. type[] copyOf(type[] original, int length) 这个方法将会把 original 数组复制成一个新数组,其中 length 是新数组的长度。如果 length 小于 original 数组的长度,则新数组就是原数组的前面 length 个元素,如果 length 大于 original 数组的长度,则新数组的前面元索就是原数组的所有元素,后面补充 0(数值类型)、false(布尔类型)或者 null(引用类型)。 4. type[] copyOfRange(type[] original, int from, int to) 这个方法与前面方法相似,但这个方法只复制 original 数组的 from 索引到 to 索引的元素。 5. boolean equals(type[] a, type[] a2) 如果 a 数组和 a2 数组的长度相等,而且 a 数组和 a2 数组的数组元素也一一相同,该方法将返回 true。 6. void fill(type[] a, type val) 该方法将会把 a 数组的所有元素都赋值为 val。 7. void fill(type[] a, int fromIndex, int toIndex, type val) 该方法与前一个方法的作用相同,区别只是该方法仅仅将 a 数组的 fromIndex 到 toIndex 索引的数组元素赋值为 val。 8. void sort(type[] a) 该方法对 a 数组的数组元素进行排序。 9. void sort(type[] a, int fromIndex, int toIndex) 该方法与前一个方法相似,区别是该方法仅仅对 fromIndex 到 toIndex 索引的元素进行排序。 10. String toString(type[] a) 该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号,和空格隔开。 11. List asList(type[] a); 该方法将一个数组转换成List集合.
15、JavaLambda表达式
Lambda表达式:带有参数变量的表达式。由参数列表,"->",Lambda主体组成。 Lambda 主体中引用的局部变量必须使用 final
修饰或事实上最终的(类似匿名内部类)
15.1 Lambda常见格式
Lambda常见使用格式
基本格式: (参数)->{ 方法体 } () -> 2; 不需要参数,直接返回2; x -> 2 * x; 接收一个参数(数字类型),返回这个参数的2倍的值; (x,y) -> x + y; 接收两个参数(数字类型),返回这两个参数的和; (int x,int y) -> x *y; 接收2个int类型的参数,返回他们的乘积; (String s) -> System.out.println(s); 接收一个String类型的字符串,并在控制台打印,不返回任何值(有点像void);
15.2 函数式接口
函数式接口:有且仅有一个抽象方法的接口。@FunctionalInterface 注解用于表示该接口会设计成一个函数式接口,函数式接口有Comparator<T>,Callable<V>,Consumer<T>等等
要实现Lambda表达式,就需要提供函数式接口, 函数式接口就是一个添加了@FunctionalInterface注解的接口,并且该接口只能有一个抽象方法。 语法格式: @FunctionalInterface interface 接口名称{ //只能有一个抽象方法 void test(); }
15.3 方法引用 "::"
-
基本语法:
目标引用 :: 方法名
-
作用:直接传递现有的方法实现
-
方法引用主要有四类(仅有一个方法调用的 Lambda 方法体)
-
指向静态方法的方法引用,
args -> ClassName.staticMethod(args)
等价ClassName::staticMethod
-
指向任意类型的实例方法的方法引用,
(arg0, rest) -> arg0.instanceMethod(rest)
等价ClassName::instanceMethod
(arg0 的类型是 ClassName) -
指向现有对象的实例方法的方法引用,
args -> expr.instanceMethos(args)
等价expr::instanceMethod
-
构造函数引用,
ClassName::new
(需要有无参构造器)
-
16、Java异常处理
Java中的异常又称为例外,是一个在程序执行期间发生的意外事故,它会中断正在执行的程序。 为了能够及时有效地处理程序中的运行异常,必须使用异常类,这可以让程序更具有好的容错性。 Java 把所有的非正常情况分成两种:异常(Exception)和错误(Error),它们都继承 Throwable 父类
异常类结构图
16.1 Throwable & Exception类
Throwable实例方法
String getMessage():返回该异常的描述信息(提示给用户) String toString():返回该异常的类型和描述信息(不用) void printStackTrace():打印异常的跟踪栈信息到控制台,包括异常的类型、异常的原因、异常出现的位置(开发和调试)
Exception类
所有的异常类是从 java.lang.Exception 类继承的子类。 Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。 Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。 Error 用来指示运行时环境发生的错误。 例如,JVM 内存溢出。一般地,程序不会从错误中恢复。 异常类有两个主要的子类:IOException 类和 RuntimeException 类。
16.2 异常类型
-
运行时异常&非运行时异常
1.运行时异常 即RuntimeException类及其子类的异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。 这些异常一般由程序逻辑错误引起的,程序更应该从逻辑角度尽可能避免产生这类异常。 NullPointerException 空指针异常 IndexOutOfBoundsExcetpion 下标越界异常 NumberFormatException 数字转换格式异常 ArithmeticExceptin 算术异常,如以0为除数 ClassCastException 类型转换异常 其它... 2.非运行时异常 即运行时异常以外的异常,类型上都属于Exception类及其子类。 从程序语法的角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。
16.3 try......catch......finally语句
try
块后面可以接零个或多个 catch
块,如果没有 catch
块,则必须跟一个 finally
块。
16.3.1 finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。 无论是否发生异常,finally 代码块中的代码总会被执行。 在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
try-catch-finally常见格式
16.4 捕获异常
单个捕获异常:
try { // 程序代码 }catch(ExceptionName e1) { //Catch 块 }
多个捕获异常:
try{ // 程序代码 }catch(异常类型1 异常的变量名1){ // 程序代码 }catch(异常类型2 异常的变量名2){ // 程序代码 }catch(异常类型3 异常的变量名3){ // 程序代码 }
16.5 throws关键字
-
throws
语句用在方法定义时声明该方法要抛出异常类型。 -
当方法抛出异常列表中的异常时,方法将不对这些类型及其子类类型的异常作处理,而抛向调用该方法的方法,由他去处理。
-
注意:当我们自定义的异常如果是继承自RuntimeException,则无需声明。但是当如果继承自Exception,就必须要明确的声明异常。
public class NoteTest1 { public static void main(String[] args) { try{ test2(); }catch (ArithmeticException e){ System.err.println("ArithmeticException异常报错:"+e.getMessage()); } } public static void test2() throws ArithmeticException{ int i=1/0; System.out.println(i); } }
16.6 throw关键字
使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
public class NoteTest1 { public static void main(String[] args) { test3(); } public static void test3() throws ArithmeticException{ throw new ArithmeticException(); } }
16.7 try...catch/throws/throw
1)throws 关键字用于声明异常,它的作用和 try-catch 相似;而 throw 关键字用于显式的抛出异常。 2)throws 关键字后面跟的是异常的名字;而 throw 关键字后面跟的是异常的对象。
示例。
throws ArithmeticException; throw new ArithmeticException("算术异常");
3)throws 关键字出现在方法签名上,而 throw 关键字出现在方法体里。 4)throws 关键字在声明异常的时候可以跟多个,用逗号隔开;而 throw 关键字每次只能抛出一个异常。throw是抛出异常,try...catch是捕获异常。
16.8 自定义异常
-
所有异常都必须是 Throwable 的子类。
-
如果希望写一个检查性异常类,则需要继承 Exception 类。
-
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
public class UserDefinedException extends Exception{ public UserDefinedException() { super(); } public UserDefinedException(String message) { super(message); } }
public class NoteTest { public static void main(String[] args) { try{ throw new UserDefinedException("自定义报错!!!"); } catch (UserDefinedException e) { System.err.println("用户自定义异常:"+e); } } }
16.9 异常处理规则
异常处理规则
1.不要过度使用异常: 对于完全已知的错误,应该编写处理这种错误的代码,增加程序的健壮性 对外部的、不能确定和预知的运行时错误才使用异常 2.不要使用过于庞大的 try 块 3.避免使用 Catch All 语句 4.不要忽略捕获到的异常
17、Java集合框架
在Java中数组的长度是不可修改的,然而我们在实际开发中有很多情况是无法事先确定数据的数量的, 那么这些数据就不适合用数组来保存,这时就需要使用集合了。 集合就像一个容器,用来存储Java类的对象,Java的集合类比我们生活的容器功能还多,其中有些是方便我们对数据进行存放和取出的。 Java集合分为Collection类型和Map类型,它们都是Java集合的根接口,这两个接口又包含了些子接口和实现类。
Java 集合框架可以分为两条大的支线:
-
Collection,主要由 List、Set、Queue 组成,List 代表有序、可重复的集合,典型代表就是封装了动态数组的 ArrayList 和封装了链表的 LinkedList;Set 代表无序、不可重复的集合,典型代表就是 HashSet 和 TreeSet;Queue 代表队列,典型代表就是双端队列 ArrayDeque,以及优先级队列 PriorityQue。
-
Map,代表键值对的集合,典型代表就是 HashMap。
-
ArrayList和HashMap都是线程不安全的,可以使用synchronizedMap()
和
synchronizedList()等方法 -
ArrayList、LinkedList、HashSet、TreeSet...都拥有各自的方法,要使用自己的方法,就要如下
ArrayList list=new ArrayList();//拥有自己和父类方法 //不能 List list1=new ArrayList();//拥有父类方法
Java集合框架结构图
17.1 Collection和Iterator接口
-
Collection接口
Collection增加方法
boolean add(object o):向集合里添加一个元素,如果集合对象被添加操作改变了,则返回true boolean addAll(Collection c)∶把集合c里的所有元素添加到指定集合里,如果集合对象被添加操作改变,则返回true
Collection删除方法
boolean remove(object o):删除集合中第一个符合条件的指定元素o,返回true boolean removeAll(Collecrion c):从该集合中删除集合c里包含的所有元素,如果删除了一个或一个以上的元素,该方法将返回true boolean retainAll(Collection c):使该集合中仅保留集合c里包含的元素(求两个集合的交集),如果该操作改变了调用该方法的集合,则该方法返回true void clear():清除集合里的所有元素,将集合长度变为0
Collection查询方法
boolean contains(object o):判断集合里是否包含指定元素o boolean containsAll(Collection c) :判断集合里是否包含集合c里的所有元素 boolean isEmpty():判断集合是否为空,当集合长度为О时返回true,否则返回falseint size() :返回集合里元素的个数
Collection其他方法
Iterator<E> iterator() :获取一个 lterator对象(迭代器) object[] toArray()∶把集合转换成一个数组,所有的集合元素变成对应的数组元素(转化Object数组时,没有必要使用toArray[new object[0]],可以直接使用toArray() ) <T〉T[ ]toArray(T[] a)∶返回一个包含此集合中所有元素的数组;返回数组的运行时类型是指定数组的类型。如果指定的数组 a 能容纳该集合,则a将在其中返回;否则,将分配一个具有指定数组的运行时类型和此集合大小的新数组(集合转化为类型T数组时,尽量传入空数组T[O])
-
Iterator接口
1、Iterator 接口用于遍历(即迭代访问)Collection 集合中的元素 2、通过把集合元素的值传给了迭代变量 3、在创建 Iterator 迭代器之后,除非通过迭代器自身的 remove() 方法对 Collection 集合里的元素进行修改,否则在对 Collection 集合进行修改后再使用迭代器进行迭代访问时,迭代器会抛出 ConcurrentModificationException 4、注意 iterator.next()不要在while的方法体内,多次使用,要使用就用变量代替,不然会报错,NoSuchElementException
Iterator接口实例方法
boolean hasNext():如果集合里仍有元素可以迭代,则返回true object next():返回集合里的下一个元素 void remove() :删除集合里上一次next方法返回的元素
集合遍历:Iterator对象(无序,有序)、forEach(无序,有序)、fori(有序)
17.2 List接口
继承自Collection接口,拥有Collection接口所有方法。 List是一个有序、可重复的集合,集合中的每个元素都有其对应的顺序索引。List集合允许使用重复元素的,可以通过索引来访问指定位置的元素。
List接口提供常用方法: Collection接口的所有方法; E get(索引); 获取集合趾指定索引位置的元素,E为集合中元素的数据类型; E set(索引,对象); 将此集合中指定索引位置的元素修改为参数对象; int indexOf(对象); 返回此集合中第一次出现指定参数元素的索引位置,如果找不到则返回-1; int lastIndexOf(对象); 返回此集合中最后一次出现指定参数元素的索引位置,如果找不到则返回-1; List<E> subList(开始索引,结束索引); 返回一个新的集合,新集合中包含原集合从开始索引到结束索引之间的所有元素,新集合是不包含结束索引对应的元素的;
List接口方法分类记忆
-
Collection接口方法,List接口也继承了
-
增加add,addAll;删除remove;修改set;查询get,indexOf,lastIndexOf;
17.2.1 ArrayList & LinkedList类
-
ArrayList类
Arraylist类实现了可变数组的大小,存储在内的数据称为元素,它还提供了快速基于索引访问元素的方法。 使用Arraylist创建的集合,允许对集合中的元素进行快速的随机访问,不过,向Arraylist中插入与删除元素的速度相对数组要慢一些。
-
LinkedList类
LinkedList类同样也是实现List接口的实现类,与ArrayList不同的是LinkedList采用的是链表结构保存对象的,这种结构的优点就是在向集合中插入或者删除元素时提供更快的效率。 如果需要频繁向集合中插入或删除元素时,使用LinkedList就比ArrayList效率要高一些。但是LinkedList类随机访问元素的效率又相对较慢一点。 基于双向链表的List、Deque接口的实现类,添加删除元素比较快
LinkList类
新增的方法: void addFirst(对象); 将指定元素添加到集合的开头 void addLast(对象); 将指定元素添加到集合的末尾 E getFrist(); 获取集合的第一个元素 E getLast(); 获取集合的最后一个元素 E removeFirst(); 删除集合的第一个元素 E removeLast(); 删除集合的最后一个元素 注意:如果需要使用LinkedList类上面新增的几个方法,集合变量的类型可以是LinkedList,不能是List。
-
两者区别
ArrayList与LinkedList都是List接口的实现类,因此都实现了List接口的所有方法,只是实现的方式有不同而已。 1. ArrayList是基于动态数组数据结构实现的,访问元素的速度优于LinkedList的。 而LinkedList是基于链表数据结构实现的,占用的内存相对较大一些,但是在批量插入或者删除元素的时候,效率要高于ArrayList; 2. 对于需要频繁快速访问的集合,推荐使用ArrayList。需要频繁向集合中插入或者删除元素时(特别是开头和结尾),推荐使用LinkedList。
17.3 Set接口
不记录元素的添加顺序,不允许元素重复的集合。也就是说Set集合中是一个无序、不能重复的集合,只是简单的把对象加入进集合而已。并且最多只允许包含一个null元素。 尽量不要修改Set集合元素中判断两个元素相等的方法用到的实例变量,否则将会导致Set无法正确操作这些集合元素; Set存储的对象必须覆写hashCode和equals
17.3.1 HashSet & TreeSet类
-
HashSet类
HashSet类是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。 HashSet是按照Hash算法来存储集合中的元素的,因此具有很好的存取和查找性能。 当向HashSet集合中存入一个元素的时候,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值, 然后根据该hashCode值决定该对象在HashSet中的存储位置。 HashSet 集合判断两个元素相等的标准:两个对象的 hashCode() 方法返回值相等,并且两个对象通过 equals() 方法比较也相等
-
TreeSet类
TreeSet类同时实现Set接口和SortedSet接口,SortedSet接口是Set接口的子接口,可以实现对集合进行自然排序。因此使用TreeSet类实现的Set接口默认情况下是自然排序,这里的自然排序指按字母、数字的升序排序。根据红黑树结构确定元素的存储位置
TreeSet类新增的方法: E fist(); 获取集合的第一个元素 E last(); 获取集合的最后一个元素 E pollFirst(); 获取并移除集合中的第一个元素 E pollLast(); 获取并移除集合中的最后一个元素 SortSet<E> subSet(fromEl, toEl); 返回一个新的集合,新集合包含原集合中fromEl对象与toEl对象之间的所有对象,结果不包含toEl; SortSet<E> headSet(toEl); 返回一个新的集合,新的集合包含原集合中toEl对象之前的所有对象; SortSet<E> tailSet(fromEl); 返回一个新的集合,新的集合包含原集合中fromEl对象之后的所有对象,注意这里会包含fromEl对象; 注意:如果需要使用TreeSet类上面新增的几个方法,集合变量的类型可以是TreeSet,不能是Set。
-
两者区别
1. HashSet是根据元素对象的hashCode值来决定存放位置,这种顺序是完全没有规律的。 而TreeSet是按自然顺序为存放的,如字母、数字的升序排序。 2. HashSet的效率要高于TreeSet,所以你创建的集合如果不需要自然排序,推荐使用HashSet。 反之如果需要自然排序,才推荐使用TreeSet。
17.4 List与Set区别
1. List集合的顺序严格的,且允许重复数据,集合会为每个元素配置唯一的下标索引。 Set集合顺序不严格的,在添加元素的时候不会去记录元素的顺序,也不允许出现重复元素,也为不会为每个元素分配下标索引。 2. Set集合的性能、效率总体上要优于List集合。所以如果你的集合不要求有严格的顺序或者需要不允许重复元素,推荐使用Set集合。 反之如果有严格的顺序要求或者允许重复数据,推荐使用List集合。
17.5 Map集合
用于保存具有映射关系的数据,元素是key-value 对(Entry) , key不允许重复
Map集合详解
Map集合是一种以”键-值“的方式定义的集合,Map集合中的每一个元素都包含一个键(key)和一个值(value),用于保存具胡映射关系的数据。 Map集合里保存着两组数组的,一级用于保存Map里面的key,另一组用于保存Map里的value,key和value可以是任何引用类型的数据。 Map的key不允许重复的,value可以重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false; Map中key和value之间的关系是一对一的关系,通过指定的key找到对应的那个唯一的value值。 所以从Map中取出数据时,只要给出指定的key,就可以取出对应的value; Map是一个接口,和Collection接口类似。
Map集合增删查改方法
object put(object key, object value):添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的 key-value 对会覆盖原来的 key-value对,返回被覆盖的value,否则返回nullvoid putAll(Map m)︰将指定Map 中的 key-value对复制到本Map中 Object remove(Object key):删除指定 key 所对应的 key-value 对,返回被删除 key 所关联的 value,如果该 key 不存在,则返回 null boolean remove(Object key, Object value):删除指定 key、value 所对应的 key-value 对(Java 8 新增) void clear():删除该 Map 对象中的所有 key-value 对 Object get(Object key):返回指定 key 所对应的 value;如果此 Map 中不包含该 key,则返回 null boolean containsKey(Object key):査询 Map 中是否包含指定的 key,如果包含则返回 true boolean containsValue(Object value):查询 Map 中是否包含一个或多个 value,如果包含则返回true boolean isEmpty():查询该 Map 是否为空(即不包含任何 key-value 对),如果为空则返回 true int size():查询该 Map 里的 key-value 对的个数 Set<K> keySet():返回该 Map 中所有 key 组成的 Set 集合(相应实现类中的内部类,不支持 add 或 addAll 操作) Collection<V> values():返回该 Map 里所有 value 组成的 Collection(相应实现类中的内部类,不支持 add 或 addAll 操作) Set<Map.Entry<K, V>> entrySet():返回 Map 中包含的 key-value 对所组成的 Set 集合,每个集合元素都是 Map.Entry 对象(不支持 add 或 addAll 操作)
常用Map方法总结
Map集合是一个无序的集合 常用方法: int size(); 获取集合的长度; isEmpty(); 判断集合是否为空 clear(); 清空集合中的所有元素 V put(key, value); 向Map集合中添加键-值,如果key已存在于Map集合中,则该value会覆盖原来key对应的value值 V get(key); 返回Map集合中指定键所对应的value值,V表示值的数据类型; void putAll(Map集合); 将指定的Map中的key-value对复制到本Map集合中 V remove(key); 从Map集合中删除key对应的键值对,返回key对应的value,如果key不存在,则返回null; Set keySet(); 返回Map集合中所有键的Set集合 Set entrySet(); 返回Map集合中所有键-值对的Set集合,此Set集合中元素的数据类型Map.Entry Collection values(); 返回Map集合中所有value组成的Collection集合 boolean containsKey(key); 查询Map中是否包含指定的key,如果包含则返回true boolean containsValue(value); 查询Map中是否包含指定的value值,如果包含则返回true
17.5.1 HashMap & TreeMap类
-
HashMap
HashMap类
HashMap与HashSet有点类似,HashMap是根据key的hashCode来进行排序。
-
TreeMap
TreeMap类
因为TreeMap实现了SortedMap接口,所以会按照key的自然排序进行排列,按字母、数字的升序进行排列。 常用方法: Object firstKey(); 返回集合第一个key; Map.Entry firstEntry(); 返回集合第一个键值对对象; Object lastKey(); 返回集合最后一个key; Map.Entry lastEntry(); 返回集合最后一个键值对对象; Map.Entry pollFirstEntry(); 返回集合的第一个键值对后并从集合中删除该键值对; Map.Entry pollLastEntry(); 返回集合的最后一个键值对后并从集合中删除该键值对; SortedMap subMap(fromKey,toKey); 返回一个新集合,从原集合中fromKey到toKey之间的所有元素,结果不包含toKey对应的键值对; SortedMap headMap(toKey); 返回一个新集合,新的集合包含原集合中toKey对应的键值对之前的所有对象; SortedMap tailMap(fromKey); 返回一个新集合,新的集合包含原集合中fromKey对应的键值对之后的所有对象,注意这里会包含fromKey对应的键值对; 注意:如果需要使用TreeMap类上面新增的几个方法,集合变量的类型可以是TreeMap,不能是Map。
两者区别:
1. HashMap是根据元素key的hashCode值来决定存放位置,这种顺序是完全没有规律的。 而TreeMap是按key的自然顺序为存放的,如字母、数字的升序排序。 2. HashMap的效率要高于TreeMap,所以你创建的键值对集合如果不需要根据key自然排序,推荐使用HashMap。 反之如果需要根据key自然排序,才推荐使用TreeMap。
17.6 Collections类
Collections类是Java提供的一个操作Set、List、Map等集合的工具类。
-
排序
void sort(list, Comparator c[可省略]); 根据指定指定Comparator来对list进行排序,就集合转换为数组,然后调用Arrays.sort(); void reverse(list); 对集合元素进行反转,逆向排序。 void shuffle(list); 对集合元素进行随机排序 void swap(list, int i, int j); 将集合中i处元素与j处元素进行对调
-
查找、替换
int binarySearch(list, object); 返回元素在集合中的下标位置,注意:使用该方法之前一定要使用sort对集合进行排序 Object max(collection); 返回集合中最大的元素 Object min(collection); 返回集合中最小的元素 void fill(list, object); 使用参数object替换指定list集合中的所有元素 int frequency(collection, object); 返回集合中指定元素出现的次数 int idexOfSubList(list1, list2); 返回list2在list1中第一次出现的位置,未找到返回-1 int lastIdexOfSubList(list1, list2); 返回list2在list1中最后一次出现的位置,未找到返回-1 boolean replaceAll(list, oldVal, newVal); 在集合中使用newVal替换oldVal
-
复制
void copy(list1, list2); 将list2中的所有元素复制到list1中
18、Java泛型&枚举
18.1 泛型
泛型从JDK1.5开始提供,泛型编译时就检查数据类型,减少了强制类型转换。
18.1.1 语法格式(方法、类、接口)
public class 类名<T1,T2...,Tn>{ //T1,T2表示类型的参数 }
public interface 接口名<T1,T2...,Tn>{ //实现此接口时就要T1确定 }
访问修饰符 static/final <T1> 返回类型 方法名(T1 t){ } //这两个的区别 访问修饰符 static/final 返回类型 方法名(List<? extends 类名> list){ }
T1不能是基本数据类型,要换成包装类。 使用方法:类<T1的数据类型> 变量名=new 类(); 接口还能被泛型类型extends
18.1.2 限制泛型可用类型
public class 类名<T1 extends 父类>{ //传来的T1类型必须是父类的子类 }
18.1.3 泛型通配符
Java中的泛型还支持使用类型通配符(?),它是作用是在创建一个泛型类对象时限制这个泛型类的类型必须继承或者实现某个类或接口。 语法格式: 泛型类名称<? extends 类>变量, 其中“<? extends 类>”作为一个整体表示类型是未知类型,当需要使用泛型对象时,可以单独实例化。 注意:我们一般不会单独使用”<?>“这种写法,这种写法其实就相当于不写泛型类型。
18.2 枚举
枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。 优点:枚举相对于静态常量来说定义更简洁。 缺点:因为枚举是不能被继承。
18.2.1 EnumSet 和 EnumMap
...表示可变参数。
EnumSet es=EnumSet.of(EColor.red,EColor.blue,EColor.green) EnumSet es1=EnumSet.ofAll(EColor.class) EnumMap.put() EnumMap.putAll()
18.2.2 为什么要有泛型?
//不使用泛型 public static void test1(){ ArrayList list=new ArrayList(); list.add(1); list.add(2); list.add(3); list.add(4); //问题一,存不同类型 // list.add("hello"); for (Object o : list) { //问题二,强制转化 Integer i=(Integer)o; System.out.println(i); } } //使用泛型 public static void test2(){ ArrayList<Integer> list=new ArrayList(); list.add(1); list.add(2); list.add(3); list.add(4); //不能存储非Integer的类型,编译时会检查,并避免强转 // list.add("hello"); for (Integer o : list) { System.out.println(o); } }
-
Arraylist 可以存放任何类型的数据(既可以存数字,也可以混入字符),因为所有类都继承自 Object 类。
-
从 Arraylist 取出数据的时候需要强制类型转换,因为编译器并不能确定你取的是字符串还是数字。
使用类型参数解决了元素的不确定性——参数类型为 String 的集合中是不允许存放其他类型元素的,取出数据的时候也不需要强制类型转换了。
19、Java文件&IO流
19.1 File类
在Java中,File类是java.io包中表示磁盘文件的对象,也就是说,如果希望在程序中操作文件和目录,则都呆以通过File类来完成。 File类本身不能访问文件内容的,如果需要访问文件内容,则需要配合使用IO流。
File类方法思维图
-
在 java.io 包下,只能操作文件和目录,不能访问文件内容本身
-
全局静态常量
-
路径分隔符:char pathSeparatorChar、String pathSeparator
-
文件名称分隔符:char separatorChar、String separator
-
-
构造器File(String pathname):将给定的路径名字符串 pathname 转换为抽象路径名来创建 File 对象File(String parent, String child):File(File parent, String child):根据 parent 抽象路径名和 child 路径名字符串创建 File 对象
19.1.1 访问文件名和目录
-
getName():表示文件名或者目录名
-
getPath():对应路径名
-
getAbsoluteFile():返回绝对路径
-
getAbsolutePath():
-
getParent():父路径
public static void fileNameOrPath(){ File file=new File("C:\\Users\\MYA20000201\\Desktop\\" + "java2022\\javaSE\\java_7.28\\src\\com\\my\\hello.txt"); System.out.println("文件名称:"+file.getName()); System.out.println("文件路径:"+file.getPath()); System.out.println("文件父目录:"+file.getParent()); System.out.println("文件绝对路径:"+file.getAbsolutePath()); System.out.println("文件绝对路径名:"+file.getAbsoluteFile()); }
19.1.2 检测文件
-
exists():判断文件或者目录是否真实存在
-
isFile():判断是否是文件
-
isDirectory():判断是否是目录
-
isAbsolute():判断绝对路径
-
canRead():判断是否可读(还有可写...)
19.2 IO流
IO流框架图
19.2.1 什么是流?
在Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。 在学习输入和输出流之前,我们要明白为什么应用程序需要输入和输出流。
Java程序通过流来完成输入、输出,所以的输入/输出以流的形式来进行处理的。 输入就是将数据从各种输入设备(键盘、扫描仪)中读取到内存,输出正好相反,是将数据写入到各种输出设备(显示器、硬盘)。
19.2.2 输入流
Java流相关的类都java.io包中,而且每个数据流都量个对象。所有输入流都是InputStream抽象类和Reader抽象类的子类。 其中InputStream类就是字节输入流的抽象类,是所有字节输入流的父类。
InputStream方法
int read() 从输入流读取一个8字节的数据,将它转换成一个0~255的整数,返回一个整数,如果遇到输入流的结尾返回-1; int read(byte[] b) 从输入流读取若干字节的数据保存到参数b指定的字节数组中,返回的字节数表示读取的字节数,如果遇到输入流的结尾返回-1; int read(byte b[], int off, int len) 从输入流读取若干字节的数据保存到参数b指定的字节数组中,其中oof是指在数组中开始保存数据位置的起始下标,len是指定读取字节的位数。返回的字节数表示读取的字节数,如果遇到输入流的结尾返回-1; long skip(long n) 从输入流跳过参数n指定的字节数量 int available() 返回可以从数据源读取的数据流的位数 void close() 关闭数据流,并释放系统资源 void mark(int readlimit) 如果输入流可以被重复读取,从流的当前位置开始设置标记,参数readlimit指定设置标记的字节数 void reset() 使输入流重新定位到刚才被标记的位置,这样可以重新读取标记过的数据。 boolean markSupported() 判断输入流是否可以重复读取,如果可以返回true
19.2.3 输出流
在Java中所有输出流都是OutputStream抽象类和Writer抽象类的子类,其中OutputStream类是字节输出流的抽象类,是所有字节输出流的父类, 用于以二进制的形式将数据写入目标设备。
OutputStream方法
void write(int b) 将指定字节的数据写入到输出流 void write(byte b[]) 将指定的字节数组的内容写入到输出流 void write(byte b[], int off, int len) 将指定的字节数组从off位置开始的len长度的内容写入到输出流 void flush() 刷新输出流,强行将绥中区的内容写入输出流 void close() 关闭数据注,并释放系统资源 注意:无法是输入流的read方法还是输出流的write之后,一定要执行close方法,用于关闭流。
19.2.4 系统流
每个Java程序运行时都带有一个系统流,系统流对应的类为System类,System类封装了3个系统流,分别是: System.in 标准输入流,默认设备是键盘 System.out 标准输出流,默认设备是控制台 System.err 标准错误流,默认设备是控制台
19.2.5 字符集编码
计算机中,任何的文字都是指定的编码方式存在的,在Java程序开发中最觉的UTF、Unicode、GBK/GB2312、ISO8859-1 1. ISO8859-1 属于单字节编码,最多只能表示0~255的字符范围。 2. GBK/GB2312 中文的国标编码,用来表示汉字,属于双字节编码。GBK可以表示简体中文和繁体中文,而GB2312只能表示简体中文。 3. Unicode 是一种编码规范,是为了解决全球字符通用编码而设计的。UTF-8和UTF-16是Unicode规范的两种实现,此编码不兼容ISO8859-1的。 4. UTF UTF编码兼容了ISO8859-1编码,同时也可以用来表示所有的语言字符,不过UTF编码是不定长编码,每一个字符的长度为1~6个字节不等。 一般在中文网页中常使用该编码。 通过System.getProperty() 获取系统属性来查看默认编码
19.3 IO流实现子类
IO流操作步骤
1.创建节点对象 2.创建 IO 流对象 3.具体的 IO 操作(read()、write()) 4.关闭 IO 流对象
-
字节流和字符流字节流操作的数据单元是 8 位的字节字符流操作的数据单元是 16 位的字符(2个字节)
19.3.1 文件流FileInput/Output/Reader/Writer
文件流主要访问文件。构造器(当指定 File 对象对应的文件不存在时,会在对应的目录下新建该文件)
-
文件流主要操作类
1、FileInputStream 字节输入流 FileReader 字符输入流 构造器:FileInputStream(File file)、FileInputStream(String name)FileReader(File file)、FileReader(String fileName)
2、FileOutputStream 字节输出流 FileWriter 字符输出流 构造器:FileOutputStream(File file, boolean append)、FileOutputStream(String name, boolean append)FileWriter(File file, boolean append)、FileWriter(String fileName, boolean append)
19.3.2 数组流(内存流)ByteArray/CharArray
1、ByteArrayInputStream字节数组输入流 CharArrayReader字符数组输入流 2、ByteArrayOutputStream字节数组输出流 CharArrayWriter字符数组输出流
19.3.3 处理流(缓冲流、转换流、对象流)
缓存流
-
缓冲区大小为 8192 个字节或字符
1、BufferedInputStream 和 BufferedReader
-
构造器BufferedInputStream(InputStream in)、BufferedReader(Reader in)
-
BufferedReader 类(字符缓冲输入流)中特有的方法
String readLine()
:读取一个文本行(换行 '\n' 或回车 '\r'),如果已到达流末尾,则返回 null
2、BufferedOutputStream 和 BufferedWriter
-
构造器BufferedOutputStream(OutputStream out)、BufferedWriter(Writer out)
-
BufferedWriter 类(字符缓冲输出流)中特有的方法
void newLine()
:写入一个行分隔符(由系统属性 line.separator 定义)
转换流
-
将字节流转换成字符流:InputStreamReader、OutputStreamWriter
-
构造器InputStreamReader(InputStream in, String charsetName)OutputStreamWriter(OutputStream out, String charsetName)
-
转换流特有的方法
String getEncoding()
:返回此流使用的字符编码的名称
对象流
-
ObjectInputStream构造器:ObjectInputStream(InputStream in)实例方法:
Object readObject()
:从 ObjectInputStream 读取对象(可能需要强转) -
ObjectOutputStream构造器:ObjectOutputStream(OutputStream out)实例方法:
void writeObject(Object obj)
:将指定的对象写入 ObjectOutputStream -
对象序列化机制允许将内存中实现序列化的 Java 对象转换成字节序列(二进制流),使得对象可以脱离程序的运行而独立存在(保存到磁盘上或者通过网络传输)
-
对象的序列化(Serialize) :将一个 Java 对象写入二进制流中
-
对象的反序列化(Deserialize) :从二进制流中恢复该 Java 对象(反序列化时必须存在对象的字节码对象)
-
支持序列化机制的对象的类必须实现 Serializable 接口(标识接口,无抽象方法)
19.4 字节流&字符流区别
1. 以Strem结尾的都是字节流,以Reader或Writer结尾的都是字符流; 2. InputStream是所有字节输入流的父类,OutputStream是所有字节输出流的父类; 3. Reader是字符输入流的父类,Writer是字符输出流的父类。 4. 读写的时候字节流是按字节读写,字符流是按字符读写。 5. 字节流适合所有类型文件的数据传输,因为计算机字节是电脑中表示信息的较小单位。字符流只能够处理纯文本数据,基本类型数据不行。 6. 在读写文件需要对内容进行处理,比如比较特殊的字符,一般会选择字符流。 7. 只是单纯的读取文件,和文件内容无头的时候,一般选择字节流。
20、Java序列化
20.1 Java序列化
Java序列化
什么是序列化?Java提供了一个种对象序列化的机制,该机制中,一个对象或者一个集合都可以被进行序列化。 以字节序列的形式存储在一个文件中,然后我们又可以通过反序列化,又从文件中取出字节序列,并转换回对象。
20.2 Object存储于文件步骤
1.确认需要序列化的类实现了Serializable接口。 2.创建一个FileInputSteam(文件输入流)对象,并通过构造参数指定文件的保存地址。 3.创建ObjectInputSteam类,并传构造参数————上面的FileInputStream对象。 4.执行ObjectInputSteam对象的readObject()方法 5.关闭流
20.3 JSON(阿里)
JSON是什么呢? JSON是一种数据格式,通过非常好的在字符串与对象之间进行转换。 JSON的标准格式有2种: 第1种为对象的格式:{"key1":"value1","key2":"value2"} 第2种为数组的格式:[{"key1":"value1","key2":"value2"},{"key3":"value3","key4":"value4"}]
JSONObject/Array
用于单个对象与字符串之间的转换,主要实现了Map<K,V>这个接口。 常用方法: JSONObject.toJSON(Java对象) 可以将一个Java对象转换为JSONObject对象 JSONObject.toJSONString(Java对象) 可以将一个Java对象转换为JSON字符串 JSONObject.parseObject(字符串) 可以将一个JSON对象字符串转换为JSONObject对象 JSONObject.parseObject(字符串, Class) 可以将一个JSON对象字符串转换为Java对象 用于数组/集合与字符串之间的转换,主要实现了List<Object>这个接口。 常用方法: JSONArray.toJSON(Java集合) 可以将一个Java集合转换为JSONArray对象 JSONArray.toJSONString(Java集合) 可以将一个Java集合转换为JSON字符串 JSONArray.parseArray(字符串) 可以将一个JSON对象字符串转换为JSONArray对象 JSONArray.parseArray(字符串, Class) 可以将一个JSON对象字符串转换为Java集合
21、Java反射
反射:在运行时期,动态地去获取一个类中的信息(类的信息、构造器信息、方法信息、字段等信息)实现Java反射的类都位java.lang.reflect包中,java.lang.Class类是Java反射机制中的核心类。
21.1 反射核心类Class
Class类是实现反射的关键,Class类的一个实例表示Java的一种数据类型,包括类、接口、枚举、注解、数组、基本数据类型和void。 Class没有公有的构造方法,Class实例都是JVM在类加载的时候自动创建的。 创建Class对象的两种方法: 1. Class clazz = 类.class; 2. Class clazz = 变量.getClass(); 3. Class clazz = Class.forName("类的完整路径"); Class类的常用方法: getName() 获取类名 isInterface() 判断是否为接口 isArray() 判断是否为数组 getSuperclass() 获取父类 isPrimitive() 判断是否为基本数据类型 newInstance() 创建一个新的实例
21.2 获取属性、方法、构造器
Class类是实现反射的关键,Class类的一个实例表示Java的一种数据类型,包括类、接口、枚举、注解、数组、基本数据类型和void。 Class没有公有的构造方法,Class实例都是JVM在类加载的时候自动创建的。 创建Class对象的两种方法: 1. Class clazz = 类.class; 2. Class clazz = 变量.getClass(); 3. Class clazz = Class.forName("类的完整路径"); Class类的常用方法: getName() 获取类名 isInterface() 判断是否为接口 isArray() 判断是否为数组 getSuperclass() 获取父类 isPrimitive() 判断是否为基本数据类型 newInstance() 创建一个新的实例 Method类用来声明类的方法,通过访问Class对象如下访问: getMethod(name) 根据方法名称来获取一个Method对象,基于公共可访问的方法 getMethods() 获取所有Method对象,基于公共可访问的方法 getDeclaredMethod(name) 根据方法名称来获取一个Method对象,基于所有方法 getDeclaredMethods() 获取所有Method对象,基本所有方法 Constructor类用来声明类的构造方法,通过访问Class的如下方法: Method类用来声明类的方法,通过访问Class对象如下访问: getConstructor(name) 根据参数列表来获取一个Constructor getConstructors() 获取所有Constructor对象
21.3 使用反射加载静态资源
-
Class 类中的
InputStream getResourceAsStream(String name)
:name 不以 '/' 开头时默认是从当前类的字节码所在的路径去加载资源,以 '/' 开头则是从 classpath 的根路径去加载资源 -
ClassLoader 类中的
InputStream getResourceAsStream(String name)
:从 classpath 的根路径去加载资源,返回所读取资源的输入流,name 不能以 '/' 开头 -
获取 ClassLoader 对象的方法
-
Class 类中的
ClassLoader getClassLoader()
:返回该类的类加载器 -
Thread 类中的
ClassLoader getContextClassLoader()
:返回该线程的上下文 ClassLoader
-
21.4 使用反射生成JDK动态代理
-
通过使用 Proxy 类和 InvocationHandler 接口可以生成 JDK 动态代理类或动态代理对象,即在程序中为一个或多个接口动态地生成实现类或创建实例
-
特点:
-
代理的对象必须有实现的接口
-
需要为每个对象创建代理对象
-
动态代理的最小单位是类(所有类中的方法都会被处理)
-
21.5 什么情况使用反射?
主要我们程序无法直接通过对象.的方式来调用方法,或者无法通过new的方式来创建对象,这个时候就可以使用反射。 像我们后面会学习到的Spring框架,就大量运行了反射机制。
22、Java多线程
22.1 进程 & 线程
什么是线程?什么又是进程? 任何一个程序一经运行起来,就会启动一个进程,我们可以通过任务管理器查看进程,也可以结束进程————就相当于结果这个程序的运行。 线程是在一个进程里面执行的单元,一个进程可以包含多个线程,每个一个线程即可以独立工作,也可协作工作。 一条线程批是是进程 中一个单一顺序的控制流,多条线程可以同时运行。 进程(Process):处于运行过程中的程序(系统进行资源分配和调度的一个独立单位),每个进程有独立的内存空间线程( Thread):进程的执行单元(CPU 调度和分派的基本单位),线程之间共享堆空间,每个线程有独立的栈空间(共享父进程中的共享变量及部分环境)
22.2 线程生命周期
-
新建状态 创建一个线程之后,该线程对象就处理新建状态。
-
就绪状态 当线程对象被调用了start()方法之后,该线程就进入就绪状态,就绪状态的线程处理就绪队列中,就等待JVM里线程调度器的调度。
-
运行状态 执行run()的时候,线程就处于运行状态。
-
阻塞状态 如果一个线程执行sleep()、wait()、await()等方法,该线程就会从运行状态进入阻塞状态。
-
死亡状态 一个运行状态的线程完成任务或者其它终止条件发生的时候,该线程就切换到死亡状态。
22.3 线程创建
创建一个线程: 1、继承Thread类 继承、new 重写run()、start() 不足:单继承,无法获取返回值 2、实现Runnable接口 实现、重写run()、创建start()里面调用Thread中的start() 不足:无法获取返回值 3、实现Callable接口+FutureTask对象(这不是正真的创建线程) //描述线程的行为,真正创建对象的是Thread 实现,重写call(),FutureTask封装该类,调用Thread的start() Future会阻碍程序,其中get方法 不足:复杂 线程安全:并发问题 ”自我理解线程,“ 为啥总有个线程落后,因为有个线程刚到,后面线程不等待。
三中创建方式对比
-
继承 Thread 类
-
线程类已经继承了 Thread 类,不能再继承其它父类
-
如果需要访问当前线程,直接使用 this 即可获得当前线程
-
多个线程之间无法共享线程类中的实例变量
-
-
实现 Runnable、Callable 接口的方式创建多线程
-
线程类只是实现了 Runnable 接口,还可以继承其它类
-
如果需要访问当前线程,则必须使用 Thread. currentThread() 方法
-
所创建的 Runnable 对象只是线程的 target,而多个线程可以共享同一个 target 对象的实例变量,所以适合多个相同线程来处理同一份资源的情况
-
22.4 处理线程并发问题
当多个线程同时访问并操作同一个资源的时候,就会出现并发问题,也就是不安全隐患。 如果解决多线程的安全性问题呢?通常有两个方法,分别是synchronized和Lock
22.4.1 synchronized
Synchronized同步 把出现同步问题的代码,加上锁 synchronized(同步锁(可以根据不同类型,实现不同锁)){ 代码块...... } 同步锁对象,对相同的锁会有关闭等待,不同的锁,不会等待。 synchronized不足:没有资源了,还在排队。
22.4.2 Lock
Lock锁 Java5新增的一个功能。例子:限时排队,超时作废。 Lock需要手动加锁,手动解锁。 Lock l=new ReentrantLock(); 手动上锁:lock(); 手动解锁:unlock(); 尝试上锁:tryLock(); 不同对象,设置不同锁。 ReentrantLock.isHeldByCurrentThread():判断是否已上锁。结合tryLock()使用。
22.5 死锁了解
就是两(多)个之间相互牵制对方线程,并无限的等待对方线程,最后处于死锁。 要避免死锁的方法:只需要多个线程操作的多个对象上锁的顺序保持一致就可以了
-
当两个线程相互等待对方释放同步监视器时就会发生死锁,死锁无法解决,只能避免
-
一旦出现死锁,所有线程处于阻塞状态,程序无法继续向下执行
-
避免死锁
-
加锁顺序:所有的线程都以同样的顺序加锁和释放锁
-
加锁时限:线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁
-
-
定位死锁:利用 jstack 等工具获取线程栈,然后定位相互之间的依赖关系,进而找到死锁
22.6 线程通信
并发模型 | 通信机制 | 同步机制 |
---|---|---|
共享内存 | 线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信 | 同步是显式进行的,即必须显式指定某个方法或某段代码需要在线程之间互斥执行 |
消息传递 | 线程之间通过显式的发送消息来达到交互目的,如 Actor 模型 | 由于消息的发送必须在消息的接收之前,因此同步是隐式进行的 |
-
Java 的线程间通过共享内存的方式进行通信
22.6.1 针对 synchronized使用Object类
wait() -> notify()/notifyAll()
-
Object 类
中用于操作线程通信的实例方法
-
wait()
:调用该方法的当前线程会释放对该同步监视器(调用者)的锁定,JVM 把该线程存放到等待池中,等待其他的线程唤醒该线程(该方法声明抛出了 InterruptedException 异常)(为了防止虚假唤醒,此方法应始终在循环中使用,即被唤醒后需要再次判断是否满足唤醒条件) -
notify()
:调用该方法的当前线程唤醒在等待池中的任意一个线程,并把该线程转到锁池中等待获取锁 -
notifyAll()
:调用该方法的当前线程唤醒在等待池中的所有线程,并把该线程转到锁池中等待获取锁
-
-
这些方法必须在同步块中使用,且只能被同步监视器对象来调用,否则会引发 IllegalMonitorStateException 异常
22.6.2 针对 Lock使用 Condition 接口
await() -> signal()/signalAll()
-
java.util.concurrent.locks 包中,Condition 接口中的
await()
、signal()
、signalAll()
方法替代了 Object 监视器方法的使用(await() 方法也声明抛出了 InterruptedException 异常) -
通过 Lock 对象调用 newCondition() 方法,返回绑定到此 Lock 对象的 Condition 对象
23、网络编程
TCP/OSI对应层次协议
常见协议:
TCP: Transmission Control Protocol的意思,意为:传输控制协议,是一种面积连接、基本字节流的传输层协议。 TCP 是位于IP之上的,保障了两个应用程序之间的可靠通信,通常用于互联网的通信和传输,也被称为TCP/IP。 UDP: User Datagram Protocol的意思,意为:用户数据报协议,位于OSI模型的传输协议,是一个无连接的协议, 所以在安全性上要差一些,容易出现丢包、错误、重复的现象。但是,它是传输速度却更快。
23.1 基于TCP协议技术
23.1.1 Socket编程
Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议 socket(套接字)源 IP 地址和目的 IP 地址以及源端口号和目的端口号的组合不同主机之间的进程进行双向通信的端点
使用TCP提供的两台设备之间的通信机制,首先由服务器端创建一个套接字,然后客户端程序创建一个套接字,并尝试连接服务器端的套接字。 当连接建立的时候,服务器端会创建一个Socket对象。客户端和服务吕就可以通过Socket对象的读写来进行通信。 主要用到的类有: ServerSocket类 创建一个服务器端的套接字,创建完之后处于等待连接的状态; accept() 用于同意并接收连接到服务器端套接字的客户端对象,该方法会阻塞线程的执行,直到接收到客户连接为止; Socket类 创建一个客户端的套接字,创建完之后可以去连接服务器端的套接字; InetAddress类 获取互联网协议(IP)地址
23.1.2 URL
使用HTTP协议(底层就是TCP的)来请求网络接口 1. 创建一个URL对象; 2. 创建一个URLConnection对象,该对象的值从上一步的URL对象通过openConeection()来获取; 3. 创建一个BufferedReader对象,读取数据; URLEncoder类,用于URL转码的工具类 方法:encode("万古第一神", "UTF-8") URLdecoder类,用于URL解码的工具类 方法:decode("万古第一神", "UTF-8")
24、数据结构 & 算法
24.1 数据结构
-
数组:是一种长度固定、可以容纳相同数据类型的结构。
1.擅长做查询和修改,不擅长做增加(可能需要扩容)和删除(移位) 2.在随机访问时性能最好
-
队列:是一种线性表结构,和我们之前学的ArrayList集合相似。 1.一种特殊的线性表,只允许在表的后端(rear)进行插入操作,在表的前端(front)进行删除操作 2.单向队列(Queue):先进先出(FIFO),只能从队列尾插入数据,只能从队列头删除数据 3.双向队列(Deque):可以从队列尾/头插入数据,也可以从队列头/尾删除数据
-
链表:链表是一种非连续、非顺序的存储结构,数据元素的逻辑是通过链表的指针地址实现的。如LinkedList就是基于链表结构存储的。 1.擅长做增加和删除(头和尾),不擅长做查询和修改 2.在执行插入、删除操作时有较好的性能 3.单向链表、双向链表
-
树:树是一种数据结构,它是由n(n>=1)个有限节点组成一个具胡层次关系的集合。TreeSet(二叉树结构)和TreeMap(红黑树结构)就是基于树结构存储的。
-
栈:是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。特点:先进后出,或者说后进先出。 1.一种运算受限的线性表,仅允许在表的一端进行插入和删除运算 2.后进先出(LIFO) 3.进栈、入栈或压栈;出栈或退栈 4.底层可以数组来存储,也可以使用链表来存储
-
堆:堆是一种比较特殊的数据结构,可以看做一棵树的数组对象。
-
哈希表 1.元素的值和索引存在对应的关系的数组 2.元素值 —> Hash 函数 —> 哈希码 —> 某种映射关系(压缩映射) —> 元素存储索引 3.可以直接根据该元素的 hashCode 值计算出该元素的存储位置,因此具有很好的存取和査找性能
-
枚举:特殊的数据结构
24.2 算法
-
递归:递归就是在方法体内部调用方法本身,产生的现象叫递归。
-
排序:针对数组、集合进行的排序,包括冒泡排序、快速排序等。