十进制(decimal)
二进制(binary)
十六进制(heaxdecimal)
八进制(octal)
发生及存在,存在及对象???
空格也是字符
注意:以后说类名的时候,如果带着包名描述,表示完整类名
注意:如果没有带包名描述的话,表示简类名
//重写equals是程序员的素养
对象在堆,方法在栈,字符串常量池在方法区
字符串常量池:所有字符串对象的所在地
垃圾回收器不会回收常量的
//类在强制类型转换过程中,如果类转换成接口类型,那么类和接口之间可以不需要继承关系
native关键字:表示调用JVM本地程序
//底层调用c++代码,有方法,无方法体
//注意:java中允许子类中出现和父类一样的同名变量/同名属性
字符编码(ASCII):人为制定的二进制和文字的对照表,清楚地描述了二进制与文字的对照关系
编译软件的默认当前路径是当前模块的上一级
char类型在Java中占用了2个字节(可以将char类型看作一个字符)
字母“a”在windows中占用1个字节,汉字在windows占用2个字节
关于 Java注释
/** 加上回车键(注意在方法之外):注释,且将方法内的关键字列出
除数为零会出现异常,除数不能为零
关于配置文件:
需要经常改变的数据,可以单独写到一个文件当中,使用程序动态获取,将来只需要修改这个文件的内容,java代码不需要改变,不需要重新编译,就可以拿到动态的信息
这种机制的文件叫做配置文件。
关于属性配置文件:
文件的内容格式是: key1=value
key2=vallue
key3=value
(key重复了,value覆盖)
(支持#号,#号是注释)
(注意:等号左右最好不要有空格)
(支持使用:冒号来代替=等号,但不建议)
叫做属性配置文件
属性配置文件的后缀为:.properties结尾,但这不是必须的(建议,java规范)
其中properties是专门存放属性配置文件的一个类
注意:java.lang.*包下的类自动导入
注意:如果一个类的equals()方法重写了,那么该类的HashCode()方法必须重写。
如果equals()方法返回的是true,那么HashCode()方法的返回值应该相同
注意:HashCode方法和equals方法使用工具“同时”生成
注意:放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法
关于序列化版本号:
序列化版本号的实际作用:
来区分 相同类名 的类,如果类名一样,java虚拟机将通过 序列化版本号 来区分
只有实现了 Serializable接口 才拥有序列化版本号
Serializable接口是为了IO流中的ObjectOutputStream类的使用
实现Serializable接口,java虚拟机会自动生成序列化版本号
序列化版本号的缺陷:
这种自动生成的序列化版本号缺点是:代码一旦确定之后,不能进行后续修改,
因为只要修改,就会重新编译,这样就会生成全新的序列化版本号,这个时候
java虚拟机会认为这是一个全新的类
注意:
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号
这样即使代码进行了修改,java虚拟机也能识别出这是同一个类
关于序列化版本号手动设置:
注意:数值随意,但请确保全球唯一性
private static final long serialVersionUID=5313154687L;
关于java语言中采用什么机制来区分类:
第一:先通过类名来进行对比,如果类名不一样,肯定不是同一个类
第二:如果类名一样,靠“序列化版本号”来进行区分。
关于transient关键字:
transient关键字:表示游离的,不参加序列化(序列化,ObjectInput(Output)StreamIO流中的概念)。
例 : public transient String name;//这是一个属性
关于final关键字:最终的,不可改的
final修饰的类无法被继承
final修饰的方法无法被覆盖
final修饰的变量只能被赋值一次
final在修饰实例变量时,必须手动赋值,不能采用系统默认值(且赋值后无法更改)
final修饰的引用指向一个对象时,无法在指向其他对象,且被指向的对象无法被垃圾回收器回收
(但被指向的对象内部内存是可以被修改的,如里面的属性)
final修饰的实例变量是不可变的,一般与static联合使用,被称为“常量”
//常量的定义语法格式:public static final 类型 常量名 = 值;
//java规范中要求常量的名字全部大写 ,每个单词之间用下划线连接
java文件中每个 类 都会生成各自的 .class 文件,而每个类中必须拥有 “主方法“( public sattic void main(String[] args)
),否则无法成功运行
java文件中只能定义一个公开的类,且公开的类名必须与java源文件的文件名“相同”。
//向控制台输出消息
System.out.println(" ");
println:输出之后并换行
native2ascii命令:jdk自带的一个程序(可以将你输入的中文得出unicode码)(注:未找到)
字符型:单引号内只能包含单个字符,这是字符型(char)
字符串:双引号内可以包含多个字符(string)
Java类库字节码路径:
Java类库源码路径:C:\Program Files\Java\jdk-11\lib\src.zip
在java中main方法始终为栈底
递归:方法调用方法本身(尽量不用,因为会耗费大量栈内存,产生栈内存溢出错误)
方法(函数):代码片段,可以完成某个特定的功能,并且可以重复使用(定义在类体当中,方法体之外)
关于方法的返回值:(返回值的数据类型必须和“定义方法时声明的返回值数据类型一致”)
(1):有返回值,返回值数据类型可以是java所有数据类型,包括基本数据类型和引用数据类型
(2):无返回值,则必须使用“void”关键字(若没有使用void关键字,则该方法调用结束后必须有一个返回值)
在JVM内存划分上有三块主要的内存空间(除了这三块还有其他的内存空间)
(1)方法内存:
方法代码片段所在区域,方法代码片段属于.class字节码文件的一部分,字节码文件在类加载的时候,将其放到了方法区当中。所以JVM中的三块主要的内存空间中方法区内存最先有数据,存放了方法代码片段
代码片段在方法区内存当中只有一份,但是可以重复调用
(2)堆内存:存放的是对象
(3)栈内存:(个人理解:三闭一开的容器,“先进后出,后进先出”,栈顶为最后进入的元素,栈帧永远指向栈顶)
栈:stack,是一种数据结构
局部变量在“方法体”中声明 ,局部变量运行时在栈中存储
先调用的方法一定最后结束(main方法最先调用,位于栈底,栈??方法的调用??顺序??)
对列:两头通(允许先进先出)
***方法参数传递的不是变量,而是变量所代表的的那个“值”,与变量没有关系,其变量所占得内存也不会有所变化
遇到大括号后,方法进行释放
数据结构:
数据结构反映的是数据的存储形态
数据结构是独立的学科,不属于任何编程语言的范畴,只不过大多数编程语言当中要使用数据结构
数据结构 + 算法(很重要,计算机专业必修的一门课程)
常见的数据结构:
- 数组
- 队列
- 栈
- 链表
- 二叉树
- 哈希表/散列表
.....
方法案例:
public static 返回值数据类型 方法名
....
原码、反码和补码:(计算机采用补码的形式进行存储)(原码和补码,反码都有“符号位”和“数值位”)
补码用二进制表示
注意:补码的补码就是原码
第一位皆为符号位(0代表正数,1代表负数)
正数的反码和补码是它本身
负数的反码:除了符号位,表示数值的二进制位全部相反。例:01000111 的反码00111000
负数的补码:包括符号位,索取数的正数的 所有二进制位全部取反,之后+1 例:01000111 的补码 10111001
ASCII码:
“a” --》97 (注:“b”--》98)
“A” --》65 (注:“B”--》66) 以此类推
“0” --》48 (注:“1”--》49)
java中进制的表示方法
十进制:不加任何表示
八进制:前面加“0”的
十六进制:前面加“0x”的
字面值:字面值就是数据,其中包含多个数据类型。
java中的整数字面值被当做int数据类型(默认为int类型)可以为整数型字面值添加后缀单位,例:L(long数据类型)
Java中的浮点数字面值默认为double数据类型,注意不会出现Java特殊机制(创建float变量方法一强制类型转换,方法二是为其添加“f或F”后缀)
自动类型转换机制:
例:int 是小容量
long 是大容量
小容量可以自动转换成大容量,被称为自动类型转换机制。
强制类型转换:
大容量转换成小容量,需要强制类型转换
强制类型转换需要加“强制类型转换符”,但是可能会损失精度
强制类型转换符:(要转换的数据类型) 字面值
转换原理:先将数字转换为二进制数,然后砍掉左面的二进制数,使现在的二进制位数与你要转换的数据类型
的二进制位数一样
当整数型字面值没有超出byte类型的取值范围,该字面值可以直接对byte类型变量赋值。(java特殊机制,方便编写)
而short(char)数据类型与上同理,赋值时只要不超过short(char)的取值范围,即可直接赋值。(注意:是整数型)
数据类型的作用:jvm在运行程序的时候给该数据类型分配多大的内存空间
数据类型包括两种:
基本数据类型:包括四大类,八小种
1)整数型:
byte,short,int,long
2)浮点型:
float,double
3)布尔型:
boolean
4)字符型 :
char
(字符串不属于基本数据类型,属于“引用数据类型”)
基本数据类型占用的内存空间大小
数据类型 字节 二进制数(1byte(字节)=8bit(二进制位))
———————————————— 最大值(正数)和最小值(负数):
| byte 1 8 | 字节转换成十进制 减 1
整数型 | short 2 16 |
| int 4 32 |
| long 8 64 |
————————————————
———————————————— 在java中数据类型的正负数区分:
浮点型 | float 4 32 | 字节数最左方的二进制位为“符号位”
| double 8 64 | 0为正数,1为负数
————————————————
————————————————
布尔型 | boolean 1 8 |
————————————————
————————————————
字符型 | char 2 16 |
————————————————
引用数据类型:
类
接口
数组
字符编码:ASCII码 二进制与字母的对照表(字典)(人为干涉的二进制与文字之间的对照关系)
a--》01100001(解码)
01100001--》a(编码)
-encoding
java运算符
+
-
*
/
%:取余
++:自增运算符
自增前置:++变量(先加一),(变量先加1,再进行表达式运算)
自增后置:变量++(后加一),(变量先进行表达式计算,再加1)
--:自减运算符(与自增同理)
java关系运算符:(与python同理)
==
>=
<=
>
<
!=
逻辑运算符:(逻辑运算符要去两边的算子都是布尔类型)
&:与 (两面的算子都为真,结果就为真)
|:或 (两面的算子只要有一个为真,结果就为真)
!:非 (取反)
^:异或 (两边的算子只要不一样,结果就为真,一样则为假)
&&:短路与(结果与逻辑与一样,只不过有短路现象)
||:短路或 (结果与逻辑或一样,只不过有短路现象)
短路与和逻辑与的区别:(短路或同理)
逻辑与:两面的表达式全部会执行(第二个表达式)
短路与:如果经过第一个表达式后就可以得出结果,那么不会执行后面的表达式(第二个表达式)
短路或:第一个表达式结果是true,后面的表达式不会执行
赋值运算符共两种:
基本的赋值运算符: =
扩展的赋值运算符: -= += *= /= %= 等同于【变量 =(变量的数据类型)(表达式)运算符 数值】
扩展的赋值运算符不会改变原变量的数据类型,其中含有强制类型转换
赋值类的运算符优先级:先执行等号右边的表达式,将执行结果赋值给等号左边的变量
关于“+”的有两种作用:
字符串连接符:当加号“+”两边有一个是字符串,就会把加号当做字符串连接符来使用,返回的是一个字符串
求和:当加号“+”两边都是数值是,当做普通加号来使用,返回的是一个数值
关于多个加号:当没有小括号的时候,表达式从左向右运算
关于String引用数据类型:
String是字符串类型
控制语句:
选择结构:
if语句:
if (表达式){}
if else
if () else if () else
if语句不加大括号时,会自动将后一条语句归纳到if语句(else同理)
switch语句:(值之间的匹配)(只支持 int 或 String类型的数据)(支持ASCII码)
switch 后小括号的“数据”和case后的“数据” 一一匹配,匹配成功,则执行其下java语句
注:switch不支持java中关系运算符与逻辑运算符
注意:如果不加break,则直接进入下一个分支执行,不进行匹配。这叫做case穿透现象
switch (支持 in 或 String类型的字面值或变量,char类型也可以) {}
//char类型也可以的原因:char类型会进行自动类型转换成int类型
//case支持合并,貌似利用了case穿透现象
//
//例:switch (1){
//case 1 : case 2 :case 3 :case 4://1,2,3,4都会执行,这就是case合并
System.out.println("ww");}
case 支持 in 或 String类型的字面值或变量 :
java语句;
break;(注意:如果不加break,则直接进入下一个分支执行,不进行匹配)
case 支持 in 或 String类型的字面值或变量 :
java语句;.......
break;
default :(注:类似else)
java语句;
.....
循环结构:
for语句: 开始 结束 开始到结束之间的变化规律
格式:for (初始化表达式;布尔表达式;更新表达式){循环体}(分号;,是必须的)
while语句:java中的while循环条件结果只能是布尔值true或flase,不能是其他值
do..while语句:
控制循环的语句:
break
continue:继续,出现在循环语句当中,控制循环的执行,跳过本次循环,进行下一次循环
三元 运算符:(三元运算符不是一个完整的java语句)(可声明数据和变量,并将三元运算符的结果进行赋值)
语法规则:
布尔表达式 ? 表达式1 :表达式2 (布尔表达式为真,执行表达式1。 为假,则执行表达式2)
import 路径:导入类
java.util.Scanner
Scanner类:
System.in:向控制台获得
获取输入方法:
next()
nextLine()
nextInt()
nextFloat()
nextDoule()
从控制台“输入”例子:
import java.util.Scanner;
public class Tun{
public static void main(String[] args){
double c;
System.out.println("请输入一个数值");
Scanner num=new Scanner(System.in);
c=num.nextDouble();
System.out.println(c);
}
}
特殊注释:
/**
* 语句
* 语句
*/
该注释可以被javadoc.exe文件解释成doc文档
java转义字符:(这是字符,属于char数据类型),反斜杠“\”在具有转义功能
转义字符出现在特殊字符前,会把特殊字符转义成普通字符
\n:换行符
\t:制表符(就是一个tab键),制表符和空格不一样,ASCII码不相同
\\:代表一个普通的反斜杠“\”字符,(第一个反斜杠为转义字符,将后面的反斜杠转义成普通的反斜杠字符)
\':代表一个普通的单引号( ' ),
\u ???:unicode编码对应的字符
变量分为:(注意,出了大括号就不认识)
成员变量:类体之内,方法体之外
实例变量:未赋值,系统自动赋值为null
静态变量:类似全局变量,在java内声明成员变量必须加“static“关键字
成员变量声明后,如果没有手动赋值,系统会自动赋值
八大数据类型的默认值都为0
局部变量:方法体之内(局部变量不会被系统自动赋值)
关于方法重载机制:
功能相似,可使用方法重载
特点:在同一个类当中,方法名相同,参数列表不同,使用时像是在使用一个方法一样
//注意:方法重载和返回值类型无关
关于方法覆盖机制(又称:override/overwrite):(重写时尽量 复制粘贴原方法)
父类中的方法无法满足子类的业务需求,这时需要进行方法覆盖/方法重写
方法覆盖后,会调用重写之后的方法
条件:
1.方法覆盖发生在拥有继承关系的父子类之间
2.返回值类型相同,方法名相同,形参列表相同
3.访问权限不能更低,只能更高
4.抛出异常不能更多,可以更少
注意:
1.私有方法不能继承,不能覆盖
2.构造方法不能继承,不能覆盖
3.静态方法不能覆盖
4.覆盖只针对方法,不针对属性
多态:
多态的产生条件:存在继承关系
//父类型引用可以指向子类型对象,
//注意,java分为编译阶段和运行阶段,在父类型引用指向子类型对象时,只能调用父类型的方法,但底层运行的却是子类型对象
java.lang.ClassCastException //类型 转换异常,只在强制类型时会发生,也就是“向下转型”
向上转型:子类转向父类型(自动类型转换)
向下转型:父类转换成子类型(需要进行强制类型转换)
避免向下转型的异常:
instanceof:用法 ( 对象1 或引用 instanceof 对象2或数据类型 )
两个对象之间数据类型的判断
//返回值类型为布尔类型
关于访问控制权限修饰符:
public :表公开的,任何位置都可以访问
protected:同包以及子类可以访问
缺省:不写修饰符 , 只在同一个包下可以访问
private:表示私有的,只能在本类中访问
修饰符范围:
private>缺省>protected>public
//注意:类只能采用public和“缺省”修饰符修饰【内部类除外】
//修饰类,变量,方法
关于this关键字:
this能出现在实例方法和构造方法中
this不能使用在静态方法中
this()只能出现在构造方法第一行,通过当前的构造方法去调用“本类”中其他的构造方法
//this()和super()不能共存
关于super关键字:super.可调用父类的属性和方法,super()可调用父类的构造方法
super能出现在实例方法和构造方法中
super不能使用在静态方法中
语法:super. 和 super()
super()只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法
//子类构造方法第一行默认super()
super()子类的构造方法调用父类的构造方法
关于抽象类(abstract):类和类之间有共同特征,将具有共同特征的类进一步抽象,形成 抽象类
//确切的说的 就是 类的再次抽象
//抽象类无法实例化,无法创建对象,所以抽象类是用来被子类继承的
//抽象类有构造方法,供子类使用
//抽象类属于引用数据类型,且支持多态
语法:
修饰符列表 abstract class 类名{
类体;
}
//final和abstract无法同时修饰类,因为fanal修饰的类无法被继承,abstract抽象类就是用来被继承的,出现冲突
//抽象类可以继承抽象类
//抽象类中不一定有抽象方法,抽象方法一定出现在抽象类中,抽象类中也可以有非抽象方法
//非抽象子类从抽象类中继承过来的抽象方法必须得重写/覆盖/实现,归根结底抽象方法只能出现在抽象类
关于抽象方法:
//没有实现的方法,没有方法体的方法
例:public abstract void doSome();
关于接口(interface):作用“解耦合”
//接口也是一种引用数据类型,接口的祖先类也是Object
//接口是完全抽象的,(抽象类是半抽象的)
//接口一般都是对“行为”的抽象
语法:
修饰符列表 interface 接口名{
}
//接口可以继承接口,且接口支持多继承 (一个接口继承多个接口)
//一个类可以实现多个接口(弥补了java中类与类单继承的缺陷)
//接口中只有“常量”和“抽象方法”
//接口中所有的元素都是public (公开的),且public abstract可以省略
//类实现接口可以使用implements关键字完成
//注意:接口与接口之间在进行强制类型转换时,没有继承关系也可以强转(接口稍稍有点特殊)
//但是运行时可能会出现ClassCastException异常
//面向抽象编程,降低程序的耦合度,提升程序的扩展力(接口的使用离不开多态)
异常处理:
try{ }
catch(){}
finally{}(一定会执行)
java.util.Date : 日期时间类
java.text.SimpleDateFormat : 简单格式化类
(已知)有参构造:参数 为输出格式 例("yyyy年MM月dd日 HH:mm:ss")
输出:
对象.format(输出格式)
对象.parse(“日期”):返回一个该日期的date对象
java.util.Random:随机数类
Java中”equals“和“==”的区别
java中“==”比较的是变量所保存的值是否相等,例:str1==str2,当前比较的是两个对象的内存地址是否相等
而不是比较两个对象是否相等
equals是重写方法
public boolean equals (Object obj) {
if(obj == null 1 ! (obj instanceof Address) ) return false;
if( this -=obj)return true;
Address a-|(Adldress) obj;
if(城市相同'&&街道相同&&邮编相同){ //属性的判断
return true ;
}
return false ;
object类内的方法:
equals方法:比较的是两个对象是否相等,尽量重写(还有toString方法)
toString方法:将对象以字符串的形式表达出来
finalize方法:不需要人为手动调用,jvm的垃圾回收器负责调用。(可以重写)(遗言方法)
只需要重写,在对象销毁时,由jvm自动调用。
hashCode方法:实际上就是一个对象的内存地址,经过哈希算法,得出的一个值。等同于对象的内存地址。
instanceof:用法 ( 对象1 instanceof 对象2 )
两个对象之间数据类型的判断
注意:垃圾回收器存在着 垃圾太少,不启动的可能性
System.gc()方法:建议启动垃圾回收机制,但是还是存在着不启动的可能性(提高启动垃圾回收机智的成功几率)
带有native关键字的方法,底层调用的是c++算法
匿名内部类:(可读性偏差,尽量不用)
内部类:在类的内部又定义了一个新的类
内部类的分类:
静态内部类:类似于静态变量(在类中且声明时前面加static)
实例内部类:类似于实例变量 (类内定义)
局部内部类:类似于局部变量(在方法内部声明其类,且只能在该方法中使用)
//匿名内部类是局部内部类的一种
匿名内部类:创建一次性的对象,//可使用在方法之中
new 父类或接口名 () { }
创建 其父类下的一个对象 构造方法的声明 里面含抽象方法的实现
数组(array):数组是一种引用数据类型,父类是Object
数组是一种容器,可以同时容纳多个元素。(数组是一个数据的集合)
数组当中可以存储基本数据类型的数据,也可以存储引用数据类型的数据
数组因为是引用数据类型,所以数组对象是堆内存当中。(数组存储在堆内存当中)
数组当中如果存储的是 Java对象 的话,实际上存储的是对象的内存地址
数组一但创建,Java中规定,长度不可变
数组分为:一维数组,二维数组,多维数组。
所有数组对象都有“length属性”,用来获得数组中元素的个数
Java中数组要求数组中的元素的类型统一。
数组中每个元素所在空间都是有规则的,连续的,有规律的。(索引??)
数组实际上是一种简单的数据结构
所有的数组中第一个元素所在空间的内存地址,是整个数组对象的内存地址。
为什么第一个元素是整个数组的内存地址???
因为可以通过第一个元素的内存地址来推算出其他元素的内存地址。
数组中的每个元素都是有下标的,由0开始,最后一个下标是 length - 1 。(存取是依靠下标类完成的)
语法格式: 引用数据类型
数组的声明: int[] 变量名;
初始化数组:
静态初始化: int[] 变量名 = {值。。。。。}
动态初始化: 变量名 = new int[数值];
数组的优缺点:
优点:查询/查找/检索某个下标上的元素时效率极高。可以说是查询效率最高的一个数据结构。
为什么检索效率高?
第一:每一个元素的内存地址在空间存储上是连续的。
第二:每一个元素类型相同,所以占用空间大小一样。
第三:知道第一个元素内存地址,知道每一个元素占用空间的大小,又知道下标,所以通过一个数学表达式就可以计算出某个下标上元素的内存地址。直接通过内存地址定位元素,所以数组的检索效率是最高的。
(数组中查找的方式是通过数学表达式计算出来的,所以不管数组内的元素有多少,查找的效率都一样)
缺点:
第一:在插入或删除元素时,会牵扯到该元素后的所有元素发生变动。
第二:数组存储大数据量时,很难找到很大一块连续的内存空间
直接传递静态数组:
方法名(new int【】{值,值,值})
jvm虚拟机去调用 main 方法,并传递一个数组参数
在cmd中 Java 文件名 asd da dad
(注意,在文件名之后的字母,jvm会 将其转换为字符串格式,放进main方法中的参数数组)
public static void main(String[] args){}
用来接收用户输入的数据的
字符串.equals(字符串对象) (这种方法可以避免空指针异常)
abc.equals(abc)
数组是支持多态的,
例: animal a = {new cat(),new bird()};
animal是cat,bird的父类(这是多态)
关于数组的扩容:
(数组扩容效率较低)最好在创建数组时,预估一下数组的长度,减少数组的扩容次数
创建一个大容量数组,将小容量数组内的 数据拷贝进去。
扩容方法:数组拷贝(拷贝的是对象的内存地址)
System.arraycopy(源头,int 源的起点,目标,int 目标的起点,int 长度);
//被拷贝 //拷贝到该目标
关于二维数组: 二维数组其实是一个特殊的一维数组,只不过一维数组中的每个元素都是一个一维数组
声明格式: 例 int[][] a;
二维数组静态初始化:int[][] b = { {0,1 },{0,1 },{0,1 } };
三维数组与二维数组的规律相似
数组中每个元素的内存空间的长度都是4
数组之算法:
排序算法:
冒泡排序算法:
相邻的两个数进行比较,最大的放后面,用到两个循环,外循环用来得到参与比较的数据中最大的数字放在最后,内循环:相邻的两个数字进行比较,交换位置,大的那位放在后面,
选择排序算法:每一次从这堆参与比较的数据中找出最小值,拿着这个最小值和最前面的元素交换位置(每一次的交换位置都是有意义的,比冒泡排序更有效率)
查找算法:
二分法查找算法:将数组从中间分成两个部分(建立在从小到大排序之后)
二分法查找只适合从小到大排序的数组,因为该查找方法是得到中间元素来和被查找元素进行对比,然后得到被查找元素在中间元素的左右方(该方法效率高于“一个挨着一个的查找方法”)
二分法终止条件:直到中间元素正好是被查找元素
(首下标 + 尾下标)/2
Java中已经封装好了,是数组工具类Arrays类
java.util.Arrays
方法:
Arrays.sort(数据类型[] array);:数组排序方法,静态方法用于排序
//工具类中的方法大部分都是静态的
Java常用类
string类:不可变,字符串存储在“方法区”的“字符串常量池”当中,因为string使用的太过频繁,所以为了效率,放在了“字符串常量池”当中(注意:凡是双引号括起来的,在字符串常量池中一定有一份,)
string类中的构造方法:
string(bytes【】):将byte数组内的数字用解码的方式表现出来
string(bytes【】,元素起始下标,长度):将byte数组内的一部分元素转换成字符串
string(char【】):将char数组转换成字符串
string(char【】,元素起始下标,长度):将char数组内的一部分元素转换成字符串
string(string)
string a = “adad”;
常用方法:
char charAt(int index):截取一个char字符
boolean contains(string) :判断前面的字符串是否包含后面的字符串
boolean endsWith(string):判断当前字符串是否以某个字符串结尾
boolean startsWith(string):判断当前字符串是否以某个字符串开始的
boolean equals(string):比较两个字符串是否相等
boolean equalsIgnoreCase(string):判断两个字符串,忽略大小写
int compareTo(string):比较两个字符串的大小
byte【】 getBytes(string):将字符串转换成byte数组
int indexOf(string):判断某个子字符串在当前字符串的第一次出现出的索引
int lastIndexOf(string):判断某个字符串在当前字符串最后出现的索引
boolean isEmpty():判断某个字符串是否为空(为“”返回true,字符串长度为0)
int length():返回字符串长度(注意数组的是length属性,字符串是length()方法)
string replace(被替换(旧的),替换(新的)):替换掉旧的字符串
string【】 split(string 界限):以“界限”拆分字符串
string substring(int index):从下标开始截取字符串(可在参数内设置结束下标)
char【】 toCharArray():将一个字符串转换为一个char数组
string toLowerCase():将当前字符串全部转换为小写
string toUpperCase():将当前字符串全部转换为大写
string trim():去除字符串前后空白
string中的唯一静态方法
static string valueOf(非字符串):将非字符串转换为字符串
关于对字符串的频繁拼接问题:因为java中的字符串是不可改的,每一次拼接都会产生新字符串。这样会占用大量的方法去内存。造成内存空间的浪费。(所以出现了StringBuffer类 )
StringBuffer:需要进行大量字符串进行拼接的操作,建议使用该类。(字符串缓冲区对象)
在创建StringBuffre时最后创建一个初始化容量,减少扩容次数,预估一下容量。
构造方法:StringBuffer()默认构造方法,默认创建一个byte【】容量为16的数组
方法:
append():在末尾追加字符串,容量不够自动进行扩容。
StrignBuilder:与StringBuffer相似
StringBuffer和StringBuilder的区别:
StringBuffer中的方法都有:synchronized关键字修饰。表示StringBuffer在多线程环境下运行时安全的
StringBuilder中的方法没有:synchronized关键字修饰。表示StringBuilder在多线程环境下运行时不安全的
八大包装类:用于将八大基本数据类型转换为引用数据类型(有时方法参数为objate,由于基本数据类型无法利用多态传入参数,所以为了方便开发,出现了八大包装类)
装箱:将基本数据类型转换为引用数据类型
拆箱:运用从父类继承的方法将引用数据类型转换为基本数据类型
自动装箱机制:可以自动转换为 integer (八大包裝类)
例:integer t = 100;
自动拆箱机制:可以自动转换为 基本数据类型类型
例:int a = t;
八大包装类的构造方法:
都支持“参数为 int 或 String 类型”
character:char的包装类
integer:
构造方法:integer(int)
integer(String)
方法:
MAX_VALUE:最大值(包装类通用,常量)
MIN_VALUE:最小值(包装类通用,常量)
static paseInt:将字符串(双引号内是数字)转换为数字,static静态方法,返回值int
paseDouble:Double包装类中的静态方法
paseFloat:Float包装类中的静态方法
static valueof():数据类型转换 ,静态方法(各大基本数据类型应该都具有该方法)
Java中为了提高效率,将【-128 - 127】之间所有的包装对象提前创建好,并放到了方法区的“整数型常量池”当中,只要是在这个区间的数据不需要再new了,且创建该区间之间的对象会自动指向整数型常量池之间的对象
缓存机制尤为重要,
java关于对日期的类
java.util.Date
其中无参数构造方法获取的是当前系统的准确时间,精确到毫秒
Date类的toString方法已经重写,返回的是一个字符串日期
有参构造
参数是long类型,从1970年1月1日 00:00:00 的毫秒数来计算
(由于是地区差异,北京时间,所以可能会出现差异)
java中关于对日期的格式化
java.text.SimpleDateFormat(将该类当成 “对日期的加工封装即可”)
构造方法 参数类型String
yyyy 年
MM 月
dd 日
HH 时
mm 分
ss 秒
SSS 毫秒
yyyy-MM-dd HH:mm:ss SSS
年 月 日 时 分 秒 毫秒
以上字母不可随便定义,其他符号可随意
1秒==1000毫秒
SimpleDateFormat类内的方法
format方法的返回值类型为String类型
对象.format(Date对象);
对象.parse(“日期”):返回一个该日期的date对象
将format方法的返回值再转换成Date类型
SimpleDateFormat的构造方法参数必须与日期字符串格式相同(如果不同则会抛出异常)
调用SimpleDateFormat中的parse(字符串)方法(注意该方法不是静态方法)
方法不通过,可以尝试抛出异常 throws Exception
System类
获取从1970年1月1日 00:00:00 到系统当前的总毫秒数
System.currentTimeMillis();返回值类型为long类型
作用:可以统计该程序所运行的时间
System.out.println() out是PrintStream类的对象
System.gc() :建议启动垃圾回收器
System.exit() :退出jvm虚拟机
Java中数字格式化
java.text.DecimalFormat
DecimalFormat类
构造方法:new DecimalFormat(“数字格式”)
#代表任意数字
,代表千分位
. 代表小数点
0 代表不够时补零
例: ###,###.##
Java中BigDecimal类
属于大数据,精度极高,不属于基本数据类型,属于Java对象(引用数据类型)
专门用于财务软件中
Java.math.BigDecimal
构造方法:new BigDecimal(数字)
方法:
add:求和方法,两个BigDecimal对象的相加,返回值为BigDecimal类型
Java中关于随机数Random类
Java.util.Random
构造方法为无参
方法:
nextInt(可设置参数):返回一个int类型范围内的随机数(如果设置参数,则0-参数范围之内,不包含参数)
Java中关于枚举类型:高版本jdk中switch也支持枚举
作用:在Java开发中,有时方法的运行结果拥有两种情况以上 ,这时就需要用到枚举类型。
true和false就是枚举的两个值
一枚一枚可以例举出来的,才建议使用枚举
枚举编译后可以生成class文件
枚举是一种引用数据类型
枚举的每一个值可以看做是常量
(枚举是一个类,里面的枚举值可以看做常量,且注意该值不需要赋值,建议单词尽量大写)
枚举的创建语法:
enum 类名{//大写字母 例:
SUCCESS,FAIL //枚举值}
枚举的调用:
类名.枚举值
例:enum Test{SUCCESS,FAIL}
Test.SUCCESS //枚举的调用
Java中对异常的回顾
程序在执行过程中发生了不正常的情况,这种情况叫做:异常
Java提供了异常处理机制,发生异常后会在控制台打印出异常信息,供编译人员参考,让程序更加的健壮。
**异常作用:增加程序的健壮性。
Java中异常是以“异常类”的形式存在,每一个类都可以创建对象
**通过“异常类”可以创建“异常对象”
//**运行时关于异常,jvm虚拟机检测到异常,会自发的创建一个对象的异常对象,并抛出,让控制台去接收
两大可抛出类:
exception和error的父类是 Throwable类(可抛出的)
Throwable的父类是Object
异常exception:
***注意:编译时异常和运行时异常都是在运行阶段发生的,只有在程序运行阶段才可以new异常对象,并让控制台接收消息
exception又分为两类:
ExceptionSubClass:Exception的直接继承子类及其子类都被叫做编译时异常
编译时异常,编译阶段前预先处理该异常,如果不处理编译器报错
发生概率较高,可以进行预处理
编译时异常又叫受检异常,受控异常
RuntimeException:所有的RuntimeException类及子类都被称为运行时异常
运行时异常:编译通过,运行时发生报错,视为运行时异常
发生概率较低,没必要预处理
运行时异常又叫未受检异常和未受控异常
错误error:发生后终止程序执行,立即退出jvm,错误不可处理
对异常的处理方式:
第一种:在方法声明的位置上,使用throws关键字,抛给上一级
第二种:使用try..catch语句进行对异常的捕捉
注意:Java中如果将异常一直上抛,抛给了jvm,这是jvm退出,终止程序。
使用throws关键字的使用案例:
public void 方法名() throws 异常类名{ }
关于catch:
catch支持使用多态
catch可以写多个,建议catch的时候,精确到一个一个,这样有利于程序的调试
注意:catch多个的时候,异常必须按照从上到下,从小到大的顺序
关于jdk8的更新:
从jdk8开始,catch后的参数类型允许采用“或”的形式,
例: catch(异常 | 异常 | 异常 e){}
关于对于异常的处理方式(throws或try,catch):
如果希望调用者来处理,使用throws
其他情况采用try {} catch(){}
不建议在main方法上使用throws
所有异常常用方法:
返回值String getMessage():获取异常简单的描述信息
常用 对象.printStackTrace():打印异常追踪的堆栈信息(采用了异步线程掉的方式 ,多线程)
在实际开发中建议多使用
关于对异常的追踪信息:
括号内为灰色是sun公司人员编写,不需要看。
观看信息从上往下一行一行看(区分,也可以选择看 包名 来区分)
关于finally字句:
在finally字句中的代码是最后执行的,且一定会执行,即使try语句内出现了异常
finally的运行权限高于return,但是要低于System.exit()(退出jvm虚拟机)
finally不可以单独编写,必须有try语句,finally必须放在最try,catch后面(可以没有catch语句)
finally常用场合:
通常在finally语句块中完成资源的释放和关闭
关于流的关闭
关于自定义异常:
1,编写一个类继承Exception或者RuntimeException
2,提供两个构造方法:无参,有参
皆可调用父类构造方法
例:class 类名 extends Exception{
public 类名(){super()}
public 类名(String str){super(str)}
}
关于方法覆盖遗漏的异常问题:
重写后的方法不能比重写之前的方法抛出更多(更广泛)的异常,可以更少。
关于final、finally、finalize的区别:
final是一个关键字。 常量 :最终的,不变的
finally也是一个关键字,和try联用。使用在异常处理机制中
finalize()是Object类中的一个方法,作为方法名出现。(遗嘱方法???)
finalize是一个标识符 。
由垃圾回收器gc负责调用的
System.gc()方法:增加垃圾回收器的启动几率
在打印信息和return;的时候
养成手动抛异常的习惯
关于集合:(集合里面可以嵌套集合)
注意:所有有的集合类和集合接口都在 java.util.* 包下
注意:所有集合继承Iterable接口的含义是,所有集合都是可迭代的
1.什么是集合?集合有什么用?
数组就是一个集合,集合其实就是一个容器。可以容纳其他数据类型的数据
集合是一个容器,是一个载体,可以容纳多个数据对象
2.集合中存储什么?
集合不能直接存储基本数据类型,也不能直接存储Java对象,集合中存储的是Java对象的内存地址,
或者说是“引用”,集合任何时候存储的都是引用
3.不同的集合区别。
在Java中每一个不同的集合,底层会对应不同的数据结构。
往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
4.什么是数据结构?
数据存储的结构就是数据结构。不同的数据结构,数据存储的方式不同
常见数据结构 例如:
数组、二叉树、链表、哈希表.....等等。
5.本章主要是掌握如何用集合类,再什么情况下选择哪种数据结构即可。
常用集合类:
new ArrayList(); //创建一个集合,底层是数组数据结构
new LinkedList(); //创建一个集合对象,底层是链表
new TreeSet(); //创建一个集合对象,底层是二叉树。
6.集合在Java中分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口是:Java.util.Collection;
collection继承了Iterable接口:
Iterable接口内置方法iterator:
返回值为Iterator 对象.iterator()
作用:返回一个迭代器对象。
(常用)继承了collection的子接口:
List接口:特点是有序可重复,存储的元素有下标(就是列表)
常用的list接口的实现类:
ArrayList:该集合底层采用了数组的数据结构(非线程安全)(最常用)
LinkedList:底层采用了双向链表数据结构
Vector:该集合底层采用了数组的数据结构(线程安全,该集合效率较低,使用很少)
Set接口:特点无序不可重复没有下标,与list相反,且set集合中没有下标,元素不可重复
常用的Set接口实现类:
HashSet:实际上该集合底层new了一个HashMap集合
TreeSet:该集合实现了Set的子接口“SortedSet”,底层的数据结构实际上是TreeMap(自定义类型如果没有定义比较规则,放入集合后无法遍历,会出现异常)
TreeSet和TreeMap的排序的两种方式:
第一:可排序集合必须得继承Comparable接口,并实现该接口中的方法compareTo,在其中定义比较规则,compareTo方法的返回值是数字(treemap)。(适合比较规则不会发生改变的)
第二:另一种方法,在创建集合时传递一个比较器,比较器必须实现java.lang包下得Comparator接口,并实现里面的比较方法(适合多个比较规则切换的,可以采用匿名内部类的方式)
一类是以键值对的方式存储元素:
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
Map接口:
1.Map集合和Collection集合没有任何关系,只是同样在集合和框架当中
2.Map集合以key和value的方式存储数据:键值对
key和value都是引用数据类型
key和value都是存储对象的内存地址
key起到主导的地位,value是key的附属品
特点:Map集合以key和value的这种键值对的方式存储元素
key和value都是存储java对象的内存地址
所有Map集合的特点都是无序,不可重复的,与Set集合存储元素的特点相同
Map接口的实现类:
HashMap:该集合底层是哈希表数据结构,是非线程安全的,底层是一个哈希表数据结构(扩容是原容量的2倍)
//HashMap的默认初始化容量是16
注意:放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法
(如果单向链表>8个节点时数据结构会变成红黑树(二叉树)数据结构,如果红黑树节点<6,那么会重新将红黑树转换成单向链表)
关于hashMap内的默认加载因子:
hashMap内的数组的默认加载因子是0.75
这表示数组在达到 75% 时进行扩容
注意:*** HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,因为达到散列均匀,为了提高HashMap集合的存取效率,所必需的
hashMap的key部分可以为空(null),但是只能有一个
Hashtable:该集合底层也是哈希表数据结构,是线程安全的(不过效率较低,使用较少)
//Hashtable的初始化容量是11,加载因子是0.75。扩容是原容量*2+1
//Hashtable的key和value不支持null
Hashtable子类Properties:继承peroperties,存储元素的时候采用键值对方式,但
key和value只支持“String”类型,不支持其他类型,Properties被称为“属性类”
TreeMap:该集合实现了Map的子接口SortedMap,该集合底层是一个二叉树数据结构,
SortedMap接口:key的特点,无序不可重复,且集合key部分的元素会自动按照大小顺序排序,称为可排序集合
//放到TreeSet或者TreeMap集合key部分的元素要想做到排序,包括两种方式:
第一种:放在集合中的元素实现java.lang.Comparable接口
当比较规则不会发生改变,是固定的时候,推荐使用Comparable接口
第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象(Comparator)
当比较规则有多个,需要频繁切换比较规则时,建议使用比较器,实现Comparator接口
//按照字典顺序升序
Collection常用方法:
(放在集合内的元素,尽量都要从写equals方法)
注意:没有使用泛型之前,Collection可以存储Object的所有子类型
使用了泛型之后,Collection只能存储某个具体的类型
集合中不可以直接存储基本数据类型,也不能直接存储java对象,只是存储java对象的存储地址
返回值boolean add(object e): 向集合内添加元素(注意:对象.add(数字)会用到自动装箱机制)
返回值int size():该方法会返回集合内部的元素个数(注意:不是获取集合的容量)
无返回值void clear():清空集合
返回值boolean contains(object e):如果该集合内包含指定元素,则返回true(注意:底层调用了equals方法,放在集合里面的元素要重写equals方法)
无返回值void remove(object e):删除集合中指定元素(底层调用了equals方法)
例:s1和s2两个不同的对象里面都是hello,集合添加s1,remove删除s2会导致s1被删除 boolean isEmpty():判断集合是否为空,为空则返回true
返回值iterator iterator():返回一个迭代器
返回值object【】 toArray(): 返回一个拥有该集合所有元素的数组
boolean addAll(coollection e):集合合并,将指定集合内的所有元素添加到该集合当中
Map集合中不支持iterator方法,iterator是所有collection通用的一种方式
关于iterator对象(迭代器对象)的方法:
(注意:集合结构只要发生改变,迭代器一定要重新获取)
boolean hasNext():如果仍有元素可以迭代,则返回true
object next():获得迭代的下一个元素
void remove():删除迭代器内的当前元素,同时会更新集合内的元素,集合内的元素也会删除(迭代器内的元素和集合内的元素都会删除,而集合对象.remove()不推荐在迭代时使用)
list特有常用方法:
void add(int index,object element):在指定下标位置添加元素(没有下标参数,则在列表末尾添加元素)
object set(int index,object element):修改指定下标位置的元素
int indexof(object o):获取指定对象第一次出现处的索引
int lastindexof(object o):获取指定对象最后一次出现处的索引
object remove(int index ):删除指定下标处的元素
object get(int index):根据下标来获取元素
Arraylist的构造方法:(Arraylist是非线程安全的)
Arraylist():关于扩容,每次扩容后的大小是未扩容前的1.5倍
无参构造,初始化默认为零,添加第一个元素时,初始化容量是10
(指定容量):初始化Arraylist集合的容量
(cllection集合):构造一个包含指定collection的元素的列表,按照collection的迭代器返回他们的顺序排列
//建议在创建数组时预估计一下数组元素的个数,初始化数组,减少扩容
//扩容会影响效率
LinkedList:采用了双向链表,LinedList集合没有初始化容量,最初什么元素都没有
Vector:
底层的数据结构是数组,Vector是线程同步的,是线程安全的。效率较低,使用较少。
扩容之后是原容量的二倍
Vector的构造方法:
Vector():无参构造,默认初始化容量是10
Map接口中常用的方法:Map<K,V>
(注意:contains方法底层调用的是equals方法,自定义类型需要重写equals方法)
void clear():清空Map集合
boolean containsKey(object key):查询Map集合内是否有指定元素
boolean containsValue(object valu):查询Map集合中是否有指定值
v get(object key):通过键来返回该建所对应的值
Boolean isEmpty():判断集合是否为空
v put(K key,V value):向Map集合内添加键值对
v remove(object key):删除键(键删除后,所对应的值也会被删除)
int size():返回该集合的元素长度
Set<k> keySet():获取Map集合所有的key,返回一个Set集合
Collection<v> value():获取Map集合所有value,返回一个Collection
Set<Map.Entry<K,V>> entrySet():将Map集合转换成Set集合
(其中Map元素的key和value通过某种方式变成了Set集合中的一个元素,转换后Set单个元素呈现格式:key=value)。单个元素的数据类型是Map.Entry<K,V>,其中Map.Entry是一个类,Entry是静态内部类,Map是外部类(内部类支持泛型)。转换为set集合,set集合中的每一个元素都是一个对象,对象内包含一个key和value
Map.Entry内的方法:
getKey():获取该对象的键属性
getValue():获取该对象的值属性
继承关系又叫“泛化关系”。
关于哈希表(散列表)结构:
哈希表又叫散列表,是一个一维数组,这个数组中的每一个元素都是一个单向链表(有点像珠子门帘)
所以哈希表是数组和单向链表的结合体
数组:在查询方面效率较高,随机增删效率较低
单向链表:在随机增删方面效率较高,查询方面效率较低
哈希表将以上的两种数据结构融合在一起,充分发挥它们各自的优点。
map.put(k,v)实现原理:
1.先将k,v封装到Node对象当中
2.底层会调用k的hashCode()方法得出hash值,然后通过哈希函数/哈希算法,将hash值转换成数组的下标,该数组下标位置上如果没有任何元素,就将Node添加到这个位置上了。如果说下标对应的位置上有链表,此时会拿着k和链表上每一个节点中的k进行equals,若果所有的equals方法返回都是false,那么这个新节点将会被添加到链表的末尾,如果其中有一个equals返回了true,那就说明该建在链表中存在,只会将该键的value进行覆盖
v = map.get(k)的实现原理:
1.先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过数组下标快速定位到某个位置,如果这个位置上什么也没有,返回Null。如果这个位置上有单向链表,那么会拿着参数k和单向链表上的每一个节点中的k进行equals,如果所有equals方法返回false,那么get方法返回null,只要其中有一个节点的k和参数k equals的时候放回true,那么此时这个节点的value就是我们要找的value,get方法最终返回这个要找的value
重点:
需要注意,使用哈希表是key需要频繁地使用hashCode方法和equals方法,所以这两个方法都需要重写
注意:同一个单向链表上所有的hash相同,因为他们的数组下标是一样的。
当同一个链表上k和k的equals方法肯定返回false,都不相等
hashCode不可以设置一个默认值,因为这样所有的k都会是同一个下标,这样与有一个纯粹的单向链表没有区别。这种情况我们称为:散列分布不均匀。
什么是散列分布不均匀:
数组内每个元素(单向链表)都是一个单向链表,而当前单向链表与数组内的其他单向链表数量差异过大是,属于散列分布不均匀(这样会影响整个哈希表的效率)
散列分布均匀需要重写hashCode()方法时有一定的技巧
关于链表数据结构:
对于链表数据结构来说:基本的单元是节点Node。
对于单向链表来说,任何一个节点Node中都有两个属性:
第一:存储的数据
第二:下一个节点的内存地址
链表的空间存储上内存地址是不连续的
优点:因为链表的空间存储上内存地址是不连续的,随机增删元素效率较高(因为增删元素不会涉及到大量元素位移)
缺点:查询效率较低,每一次查找某个元素的时候都需要从头节点开始往下遍历(因为不能通过数学表达式计算 被查找元素的内存地址)
Node是一个类,有两个属性,分别是数据和下一个节点(Node)的内存地址
关于双向链表:
双向链表的的基本单元还是节点Node
只不过节点(Node)内有三个属性
第一:上一个节点的内存地址
第二:数据
第三:下一个节点的内存地址
源码中,创建第一个节点后,first(首节点)和last(末尾节点)都为第一节点的内存地址
关于二叉树数据结构:
Java二叉树内拥有五个属性:
k,v,left(左子节点),right(右子节点),父节点
自平衡二叉树(TreeSet、TreeMap):(以第一个数字作为“根”)
特点:遵循左小右大原则存放
遍历二叉树的三种方式:(注意:前中后说的是‘根’的位置,以根的位置为标准)
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
)
集合类之间的转换:
通过集合的构造方法来进行转换,构造方法的参数可以传递另一个集合,来进行集合之间的转换
collection集合工具箱 :
java.util.collections;包下的的collection集合工具箱
排序:(集合之间支持互相包装)
Collections.sort(list); //注意比较对象需要实现 比较接口(Comparable)并实现比较方法
Collections.sort(list,比较器对象); //也可以传入比较器对象(Comparator)
List<String> list = new Array<>(set); //(集合之间支持互相包装),将set集合转换成了ArrayList集和
Collections.sort(list)
关于如何将一个线程非安全的ArrayList集合转换成线程安全的:
collections.synchronizedList(集合对象):将非线程安全的集合转换为线程安全的
Hashtable子类Properties的常用方法:
setProperty(key,value):存储键对值
getProperty(key):通过建获取对象
load(输入流):将硬盘文件加载到properties对象上
关于泛型:(感觉起到了一个过滤的作用)
泛型是程序编译阶段起的作用,只给编译器参考
用泛型来指定集合中存储的数据类型,这样集合中元素的数据类型变得更加统一了,如果存储非指定元素,编译器会报错
优点:
第一:集合中存储的元素类型统一了
第二:从集合中取出的元素类型示泛型指定的类型,不需要进行大量的“向下转型”!
缺点:
使集合存储的元素缺乏了多样性
例:list<Animal> 变量 = new Animal<Animal>();
list<Animal>:使用泛型之后,表示List集合中只允许存储Animal类型的数据。
jdk8之后泛型进行了优化,右侧的Animal<>尖括号内可以不写,这是自动类型推断,又叫钻石表达式。
关于自定义泛型:创建类时可以自定义泛型
//定义了自定义泛型,却不用,将默认为Object
例://使用时该类可以使用泛型
public class 类名<标识符>{
public void 方法名(标识符 参数名){
}
public 标识符 方法名(){
}
}
关于foreach(增强for):
//缺点:没有下标
for(元素类型 变量名 : 数组或集合){
语句块;
System.out.println(变量名);
}
关于位运算:
>> 数字:位运算符,二进制右移 指定位数 例,>>2,二进制右移二位,00000010变成了00000001(规律:除以2的指定位数的次方)
<< 数字:位运算符,二进制左移 指定的位数 (乘以2的指定位数的次方)
关于IO流:通过IO可以完成硬盘文件的读和写
注意:能用记事本打开的都是文本文件
输出流最后一定要刷新flush()
编译软件的默认当前路径是当前模块的上一级(project)
所有的流都在java.io包下
(很重要)流使用完一定要关闭,不然会一直占用资源
注意:在java中只要“类名”以Stream结尾的是字节流,以reader和writer结尾的是字符流。
通过IO来完成对文件的读和写
节点流和包装流:
节点流:当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
包装流:外部负责包装的这个流,叫做:包装流,还叫做:处理流。
注意:对于包装流来说,只需要关闭最外部流,节点流会自动关闭
流的分类:
输入流,输出流
字节流,字符流
按照流的方向进行分类:
以内存为参照物:
将文件从硬盘导入内存的过程叫做“输入(input)”(输入流InputStream)
将文件从内存写入硬盘的过程叫做“输出(Output)”(输出流OutputStream)
还有一种方式是按照读取数据方式不同进行分类:
按照字节的方式读取数据:
每次读取1个字节,等同于一次读取8个二进制位这种流是万能的,包括:文本,图片,声音文件,以及视频
按照字符的方式读取数据,一次读取一个字符,这种只是为了方便读取普通文本文件而存在,仅限于文本文件(限制颇多,但对文本文件非常友好)
Java IO流的四大抽象类:
Java.io.InputStream 字节输入流
java.ioOutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
都实现了Closeable接口,其中方法close()作用是--关闭流
注意:使用完流后一定要使用close()方法关闭流,不然会一直占用内存资源
所有输出流都实现了Flushable接口,且都有flush()方法(可刷新的),作用是--清空管道,将管道内的残存数据强行清出管道
注意:用完输出流后一定要使用flush()方法刷新一下,以防丢失数据
Java.io包下需要掌握的流有16个:
文件流:
java. io.FileInputstream
java.io.Fileoutputstrean
java.io.FileReader
java.io.Filewriter
转换流:将字节流转换成字符流
java.io . InputStreamReader
java .io.OutputStreanWriter
缓冲流:
java .io.BufferedReader
java .io. Buffereclwriter
java. io.BufferedInputstream
java.io.Bufferedoutputstream
数据流:
java.io. DataInputstream
java. io.Dataoutputstrean
对象专属流:
java.io. ObjectInputstrean
java.io.objectoutputstrean
标准输出流:
java . io . Printwriter
java.io.Printstream
FileInputstream详解:文件字节输入流(万能流)任何类型文件都可以采用这个流来读(硬盘-->内存)
构造方法:Fileinputstream(文件路径)(注意:路径中/可以代替\\)
方法:
read():返回值为int(读取到的是字节本身,如果读到文件末尾且无任何数据则返回-1) 读取文件的一个字节
缺陷:一次只读取一个字节,效率较低
read(byte[] b):返回值是int类型,是读取到的字节数量。一次读取b.length个字节,去往byte[]数组当中读取(可以读取多个字节,提升效率,如果没有读到字节则返回-1)
参数b:byte数组需要初始化容量,注意,读的时候数组已满则从数组头部进行覆盖
数组转换为字符串:
new String(byte数组,0,读取到的字节数量)
close():关闭流
available():返回流当中剩余的没有读到的字节数量,返回值为int,
//创建数组是可以直接new byte[对象.available()]创建于文件相同字节数的数组,只适合小文件,不适合大文件(因为byte数组不能太大)
skip(long n):返回值是long类型,以当前读取到的字符串位置为标准,跳过n个字节不读
FileOutputStream详解:从内存向硬盘写入的过程
构造方法:
new FileOutputStream(“路径文件”):若文件不存在则新建,若有文件,写入时会对源文件进行覆盖
new FileOutputStream(“路径文件”,true):以追加的方式写入字节
方法:
write(byte[] b,0,可选参数 读取到的字节数):写入于byte数组.length等量的字节(默认将源文件覆盖掉)
write(byte[] b):将byte数组的内容写入(默认将源文件覆盖掉)
write():写入单个字节
FileReader:与FileInputStream类大致一样,不过构造方法底层调用的是char数组(文件字符输入流,仅限普通文本文件,word文件不可以)
方法大致一样
//普通文本文件:可以用记事本打开正常编辑的
FileWriter:与FileOutputStream类相似度极高,方法也大致一样(文件字符输出流,仅限普通文本文件,word文件不行)
write(String str):写入字符串
write(char[] char,索引下标,字符长度):写入字符串
write(char[] char):写入char数组
BufferedReader:缓冲输入流,带有缓冲区的字符输入流,不需要自定义“char数组”
/**感觉有点类似包装类**/
构造方法:
new BufferedReader(reader n) // reader 是一个流,抽象类,该构造方法可以传进去一个字符流
//InputStreamReader转换流 可以将字节流转换成字符流
方法:
String readLine():读取一个文本行,但是不带有换行符。返回值是一个字符串,当没有读取到字符时,返回null
read():读取一个字节
BufferedWriter:(缓冲输出流),带有缓冲的字符输出流
节点流和包装流:
节点流:当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
包装流:外部负责包装的这个流,叫做:包装流,还叫做:处理流。
注意:对于包装流来说,只需要关闭最外部流,节点流会自动关闭
InputStreamReader:(转换流)可以将字节流转换成字符流,通过构造方法
构造方法:
new InputStreamReader(字节流 InputStream);
OutputStreamWriter:(转换流)可以将字节流转换成字符流,构造方法参数传递节点流
DataOutputStream:(仅了解,使用较少)数据专属流,DataOutputStream写的文件只能使用DataInputStream去读,且需要知道写入的顺序
//作用:可以将数据连同数据的类型一并写入文件。
//注意:这个文件不是普通文本文档(记事本打不开)
构造方法:
DataOutputStream(outputStream n):参数是 字节输出流(这是节点流,外面的是包装流)
方法:
.writeByte(byte n):把数据以及数据的类型一并写入到文件当中
.write数据类型(数据类型 n):写入方法含多个,与数据类型的数量相同
DataInputStream:(仅了解)
注意:读的顺序必须和写入的顺序一样,才可以正常取出数据
方法:
.read数据类型():读取数据及数据类型
PrintStream:(标准输出流)标准的字节输出流,默认输出到控制台
PrintStream ps = System.out (system.out其实是PrintStream,而println是PrintStream中的方法)
注意:标准输出流不需要手动close()关闭
构造方法:
PrintStream(outputStream n):OutputStream是一个节点流
System.setOut(PrintStream n):修改输出方向,将输出方向改为一个PrintStream文件
ObjectInputStream:(反序列化)将硬盘中的多个java对象数据块组合起来,并重新恢复到内存中。
构造方法:
ObjectInputStream (InputStream):包装流,参数是节点流
方法:
readObject():读取对象
关于一次性序列化多个对象:
将对象放入集合当中,序列化集合
ObjectOutputStream:(序列化)将内存中的java对象数据信息以切割多块并且有编号的形式(拆分对象)放到硬盘当中,叫做序列化。(这是一个包装流,节点流必须是OutputStream)
**注意:可序列化对象必须实现“Serializable”接口(该接口内无方法,只是一个标志接口,起到一个标志的作用)
注意:**********
凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号
这样即使代码进行了修改,java虚拟机也能识别出这是同一个类
关于序列化版本号手动设置:
注意:数值随意,但请确保全球唯一性
private static final long serialVersionUID=5313154687L;
注意:存取多个对象时,需要用到集合,不然存到第二个对象时会报错。
注意:如果要某一个属性不参加序列化,需要用到 transient 关键字,
“transient”关键字:表示游离的,不参加序列化。
例:例 : public transient String name;//这是一个属性
方法:
writeobject(Object obj):写入硬盘当中
writeobject(arraylist集合):支持参数是Arraylist集合,序列化集合
注意:Arraylist集合实现了Serializable接口
关于File类:
流的构造方法支持传递File类
File类与流没有任何关系,所以File类不能完成文件的读和写
File是一个文件和目录路径名的抽象表示形式
一个File对象可能对应的是目录路径,也可能是文件
构造方法:
File(String 路径)
方法:
.exists():判断该文件是否存在
.createNewFile():以文件形式新建该文件
.mkdir():以目录形式新建该文件
.mkdirs():以多重目录的形式新建
.getParent():获取该文件的父路径
.getAbsolutePath():获取绝对路径
.endswith(“参数”):是否以该参数结尾,返回值为boolean
.getName():获取文件名
.isDirectory():判断是否是个目录
.isFile():判断是否是个文件
.lastModified():获取文件最后一次的修改时间(返回值long,返回的是从1970年到现在的毫秒数)
.length():获取文件大小(返回的是文件字节长度)
.listFile():返回值是File[ ]数组,获取当前目录下的所有子文件
关于接口的两种表现方式:
普通接口:
标志接口:
java虚拟机看到标志接口可能会进行特殊待遇
例:Serializable标志接口,java虚拟机看到后,会对 对象自动生成序列版本号
关于多线程:
扩:
单核CPU表示只有一个大脑,不能够做到真正的多线程并发,只是在多个线程之间频繁切换
多线程机制是为了提高程序的处理效率
有时候栈的数量就代表了线程的多少
主线程=主栈,分支线程=支栈
主栈结束不代表分栈结束
线程优先级较高的抢到的cpu时间片相对多一些(处于运行状态的时间多一些)
注意:run()方法中的异常不能throw,只能try/catch,因为run方法在父类中并没有抛出异常,子类不能比父类抛出更多异常
关于进程:一个应用程序(可以理解为一个软件)
进程与进程:
进程与进程之间是独立的,不共享资源
关于线程:一个进程的执行场景/执行单元。
一个进程可以启动多个线程
线程与线程的关系:
**********堆内存与方法区内存共享,但栈内存独立,一个线程一个栈
多线程并发:
如:十个线程有十个栈空间,每个栈之间,互不干扰
实现线程的两种方式:
#第一种方式
编写一个类,直接继承java.lang.Thread(线程类),重写run方法
run方法中的方法体运行在分支线程中(分支线)
线程启动方法:
new 一个线程对象
********* threadObject(线程对象).start():启动一个分支线程,在jvm开辟一个新的栈空间(任务完成之后就会结束,作用仅开辟一个新的栈空间)
启动线程成功之后会自动调用run方法,并且run方法在分支栈的栈底部。(run和main是平级的)
注意:如果直接调用run方法,main和run方法在同一个栈,没有开辟新的栈空间
#第二种方式
编写一个类,实现java.lang.Runnable接口。并实现run方法 //创建一个可运行的类
实际上是将实现java.lang.Runnable接口的类实例化,并将其作为参数传递给
Thread类的构造方法,在调用Thread类中的“start()”方法。
(建议使用接口方式,在实现接口的同时,还可以继承其他类)
//也可以采用匿名内部类的形式创建一个可运行的类
#实现线程的第三种方式:(效率较低)
#实现Callable接口
这种方式实现的线程可以获取线程的返回值(这也是该方式的特点)
public Object call(){} //相当于run方法
如何使用:
第一步:需要创建一个“未来任务类对象”,FutureTask对象,构造方法参数是Callable才有返回值
第二步:实现Callable中的call方法(call方法相当于run方法)
第三:创建一个Thread对象,将FutureTask对象作为参数传递进去,然后启动线程
在主线程获取线程的返回结果
“未来任务”对象.get():另一线程的执行结果
注意:get()方法可能会导致当前线程的堵塞,因为get()方法的获取结果需要等待另一个线程的结束并且返回执行结果
线程的生命周期:
新建,就绪,运行,堵塞,死亡
抢夺执行权(cpu时间片)
新建状态,刚创建的一个线程对象
就绪状态又叫做可运行状态,表示当前线程具有抢夺CPU时间片的权利(CPU时间片就是执行权),当一个线程抢夺到CPU时间片之后,就会开始执行run方法,run方法开始执行标志着线程进入运行状态
运行状态,当之前战友的CPU时间片用完之后,会重现回到就绪状态继续抢夺CPU时间片 ,当再次抢到CPU时间之后,会重新进入run方法接着上一次的代码继续往下执行
阻塞状态,遇到阻塞事件(例如:用户的输入),当一个线程遇到阻塞事件,会进入到阻塞状态,阻塞状态的线程会放弃之前占有的CPU时间片
死亡状态,run方法结束
方法:
线程对象.setName:设置线程名字
线程对象.getName:获取线程名字
Thread.currentThread():该方法获取当前执行线程对象并返回,返回值是当前线程线程对象 Thread类型,该方法是静态方法
Thread.sleep(数值):(静态方法),让当前线程进入休眠,参数以毫秒数为单位,实际上是进入堵塞状态,放弃占有CPU时间片,让给其他线程
注意:该方法需要抛出异常
线程对象.interrupt():终止该线程对象的睡眠(sleep),
在另一个线程调用,使沉睡线程苏醒
线程对象.stop():强行结束该线程的执行(不建议使用,容易丢失数据)
合理的终止线程的执行:
在线程对象内放入一个boolean标记,如果为false,返回return;
在其他线程直接修改布尔标记就可以终止线程
关于线程线程的调度:
常见的线程调度模型:
抢占式调度模型:
那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些
//CPU时间片多一些就是运行状态的时间多一些
java采用的就是抢占式调度模型
均分式调度模型:
平均分配CPU时间片。每个线程占有的CPU的时间片时间长度一样。
关于线程调度的方法:都是实例方法(最低优先级1,最高是10,默认5,有优先级常量属性)
//优先级比较高的,抢到的CPU时间片的概率就高一些/多一些,
//CPU时间片多一些就是运行状态的时间多一些
.getPriority():返回线程的优先级,返回值int
.setPriority(int newpriority):设置线程优先级(实例方法)
.yield():让位方法(静态方法),暂停当前正在执行的线程对象,并执行其他线程,不是堵塞,只是返回就绪状态
.join():(实例方法),合并线程,当前线程停止(阻塞),让位给该实例线程
(实例线程合并到当前线程,当前线程受阻,实例线程执行)
注意:不是栈的合并,而是栈之间发生了等待关系
*****关于多线程并发环境下,数据的安全问题:
安全问题发生的三个条件:
一:多线程并发
二:有共享数据
三:共享数据有修改的行为
(只有满足以上三个条件之后,才会存在线程安全问题)
解决线程安全问题:
线程排队执行(不能并发)
线程同步机制:用排队执行解决线程安全问题
(线程同步就是线程排队,这样会牺牲一部分效率)
线程同步机制的语法:(在不得已的情况下再使用线程同步机制)
解释:线程t1和线程t2,在线程t1执行的时候,必须等待t1线程执行结束,或者说在t2执行的时候,必须等待t1线程执行。
//这门拦截的是共享数据(共享对象??)
//有点像 将门变窄了,只能通过一个人(线程),其他人(线程)只能在后面排队
synchronized(线程共享对象){
线程同步代码块;
//()填入需要排队的共享对象
//示例:t1线程,t2线程需要同步,所以填入t1和t2的共享对象(共享对象:多个线程都拥有的对象,共享数据)
}
java中有三大变量:
实例变量:在堆中
静态变量:在方法区中
局部变量:在栈中
局部变量和常量不会出现线程安全问题,因为局部变量不共享(一个线程一个栈),局部变量在栈中
线程之间 堆和方法区都是共享的 ,所以实例变量和静态变量可能存在线程安全问题
synchronized关键字的用法:
//每个Java对象都有一把锁(标记),当对象锁被一个线程所占有,另一个线程是无法再次占有,当当前线程的同步代码块结束,这把锁才会被释放
//线程进入锁池寻找共享对象的对象锁,会释放之前占有的CPU时间片,没找到在锁池等待,找到了会进入就绪状态继续抢夺CPU时间片
注意:synchronzied最好不要嵌套使用,容易造成死锁现象()
一:偏灵活
synchronized(线程共享对象){
线程同步代码块;
//()填入需要排队的共享对象,不可以为“null”
//示例:t1线程,t2线程需要同步,所以填入t1和t2的共享对象(共享对象:多个线程都拥有的对象,共享数据)
}
二:在实例方法中使用(缺点:共享对象固定为this,且整个方法都会同步,影响效率)
例:
public synchronized void 方法名(参数)
{
代码块是整个方法体
}
//当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三:类锁(保证静态变量的安全,静态的且synchronized的方法也是类锁)
在静态方法上使用Synchronized,表示找类锁
如何设置守护线程:
线程对象.setDaemon(true):将该线程设置为守护线程
线程部分-关于Object中的wait和notify方法:(生产者和消费者)
注意:以上两种方法不是线程对象的方法,是Java对象都有的方法
调用方法时,是通过Java对象调用的
注意:*****wait方法和notify方法建立在synchronized线程同步的基础之上
wait():
Object o=new Object()
o.wait();
表示(作用):让正在o对象上活动的线程进入等待状态,无期限等待,知道被唤醒为止。
*****调用方法后,会让“当前线程”(正在o对象上活动的线程)进入等待状态
直到最终调用o.notify方法()
*****进入等待状态是,会释放之前占有的o对象的锁
o.notify():
将正在进行o对象等待的线程唤醒(该方法不会释放o对象的锁)
o.notifyAll():
唤醒o对象上处于等待的所有线程
扩:
生产者和消费者模式
一个线程负责生产,一个线程负责消费,最终达到生产和消费的均衡
关于线程分类:
线程分为“用户线程”和“守护线程”
用户线程:平时常用的,例:main方法,
守护线程:在后台默默运行的线程(后台线程),例如Java中的垃圾回收机制
守护线程的特点:
一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束
即使是死循环,也会随着用户线程的结束而结束
如何设置守护线程:
线程对象.setDaemon(true):将该线程设置为守护线程
关于死锁:
线程死锁是只两个或多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,互相等待对方释放资源,如果线程都不主动释放锁占有资源,将会导致死锁。
如何使用其他方法来解决线程安全问题:
//用户吞吐量就是并发量
1.尽量使用局部变量代替“实力变量和静态变量”
2.如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了(对象不共享,没有数据安全问题)
3.使用synchronized关键字
关于常用的线程调度模型(仅了解):
抢占式调度模型:
那个线程的优先级比较高,抢到的cpu时间片长的概率就高一些/多一些
java采用的就是抢占式调度模型
均分式调度模型:
平均分配cpu时间片。每个线程占有的cpu时间长度一样。
有一些编程语言,线程调度模型是采用的这种方式
关于线程同步和异步编程模型(仅了解):
异步编程模型(并发):
线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1
谁也不需要等谁,其实就是:多线程并发。
同步编程模型(排队):
线程t1和线程t2,在线程t1执行的时候,必须等待t1线程执行结束,或者说在t2执行的时候,必须等待t1线程执行。
关于定时器:(可以将定时器当做一个线程)
java类库中的Java.util.Timer
定时器的作用:间隔特定的时间,执行特定的程序
构造方法:
无参构造
Tiemr():创建一个定时器
Tiemr(String name):可以给定时器设置一个名字
Timer(true):以守护线程的方式出现
Timer(true,String name):以守护线程的方式出现,并设置一个名字
方法:
schedule(TimerTask 任务,Date 第一次执行的时间,Long 间隔的时间):安排指定的任务在指定的时间开始进行重复的,固定延迟执行
//TimerTask实现了Runnable接口,算是一个线程,TimerTask是一个抽象类
关于Java中的反射机制:(Class对象相当于“数据类型”)(SSM框架底层运用到了反射机制)
作用:通过Java中的字节码可以操作字节码文件(可以读和修改字节码文件,也就是class文件)
//可以让程序变得更加灵活
反射机制的相关类所在位置:
Java.lang.reflect.*;
反射机制相关类有哪些:
java.lang.Class;//代表字节码文件,代表一个类型,代表整个类
Java.lang.reflect.Method;//代表字节码中的方法字节码,代表类中的方法
Java.lang.reflect.Constructor;//代表字节码中的构造方法字节码,代表类中的构造方法
Java.lang.reflect.Field;//代表字节码中的属性字节码,代表类中的成员变量(静态变量和实例变量)
获取Class的三种方式:需要先获取到Class文件实例
(第一种方式)
static forName(String className):返回一个与字符串名的类或者接口相关联的Class对象
(这个方法的执行会导致类加载,而类加载会执行该类的静态代码块)
//类加载:将class文件加载到方法区
classname:需要一个完整类名,完整类名必须带有包名。(java.lang包也不能省略)
示例:
Class c1=Class.forName(“java.lang.String”)
//现在c1代表String文件(代表String.class文件,字节码文件)
(第二种方式)
Object中有一个方法(所有对象都有):该方法属于反射机制获取Class的第二种方式
.getClass():返回一个Class,作用:返回该对象类型所代表的的字节码文件(.class)内存地址
(第三种方式)
任何类型(包括基本数据类型)都有.class属性,返回的是其字节码文件地址
示例:
Class c1=String.class //c1指向“java.lang.String”class文件
Class类方法:
static forName(String className):返回一个与字符串名的类或者接口相关联的Class对象
示例:
Class c1=Class.forName(“java.lang.String”)
//现在c1代表String文件(代表String.class文件,字节码文件)
.newInstance():(实例方法)通过Class文件创建对象实例(该方法内部调用了无参构造,该类型必须有无参构造方法)
.getName():返回值String类型,返回该Class对象的完整类名(带有包名)
.getSimpleName():返回值String类型,返回该Class对象的简类名(不带有包名)
.getFields():(实例方法)返回值是Fields数组,获取类中所有public修饰的属性
.getDeclaredFields():(实例方法)返回值是Fields数组,获取类中所有的属性
.getDeclareField(String name):(实例方法)返回值是Field,参数是属性的名字,通过属性名获取指定属性
.getDeclaredMethods():(实例方法)返回一个Method数组,获取所有的方法
.getDeclaredConstructors():(实例方法)返回一个Constructor数组,获取该类所有的构造方法
.getDeclaredConstructor(Class... parameter【int.class等等】):(实例方法),返回一个指定的构造方法对象Constructor
.getSuperclass():返回值是一个Class对象,获取当前对象的父类
.getInterfaces():返回值是一个Class数组,获取当前对象的所有接口
.isAnnotationPresent(Class 注解名.class):判断该类上面是否有该注解
.getAnnotation(Class 注解名.class):获取该类上的指定注解
Field方法(属性):
.getName():(实例方法)获取该属性对象的名字
.getType():(实例方法)返回一个Class对象,返回该属性所对应的类型
.getModifiers():(实例方法)返回一个int类型,获取该属性所对应的修饰符列表(public...)
Modifier.toString(int mod):(Modifier静态方法)将所对应的数字转换为修饰符,返回值是String
//可与Field的实例方法.getModifiers()联合使用
.set(指定对象,值):(实例方法)给指定对象 赋该实例属性的属性值
.get(指定对象):(实例方法)获取指定对象 该实例属性的属性值
.getAccessibleField(true):打破封装,设置为true后,可以通过方法直接访问或者设置私有属性
//如果没有打破封装,私有属性是无法进行设置或访问的
Method方法(方法):
.getName():(实例方法)返回值是String,获取该方法的方法名
.getReturnType():(实例方法)返回值是Class,获取该方法的返回值数据类型
.getModifiers():(实例方法)返回值是int,获取修饰符列表
可以与Modifier.toString()方法连用
.getParameterTypes():(实例方法)返回值是一个Class数组,获取所有的参数类型
.getParameterType(String name,Class... parameter):返回值是一个Method对象,获取指定方法
//第一参数,要获取方法的方法名
//第二参数,要获取方法的参数列表,采用了可变长度参数,参数之间用逗号隔开,类型是Class
.invoe(指定对象,实参...):通过反射机制,指定对象调用方法
Constructor方法(构造方法):
.getModifiers():(实例方法)返回一个int类型,获取该属性所对应的修饰符列表(public...)
Modifier.toString(int mod):(Modifier静态方法)将所对应的数字转换为修饰符,返回值是String
//可与Field的实例方法.getModifiers()联合使用
getParameterTypes():(实例方法)返回值是一个Class数组,获取所有的参数类型
.newInstance(构造方法实参):根据构造方法创建一个对象
反射机制的灵活性:反射机制与属性配置文件搭配使用
Java代码写一遍,在不需要改变原代码的基础上,通过改变属性配置文件的内容可以完成不同对象的实例化
关于“可变长度参数”:
//注意1:可变长度参数要求的参数个数是:0~N个
//注意2:可变长度参数必须在参数列表的最后一个,且只能有一个
//注意3:可变长度参数可以当做一个数组看待,有下标,有length属性,也可以直接传递一个数组
语法:类型... //注意:一定是3个点
示例:
public static void m(int... args){语句块;}
关于注解(annotation):注解在程序中等同于一种标记,程序的注释
注解是一种引用数据类型,编译之后也会生成.class文件
注解语法:
【修饰符列表】 @interface 注解类型名{
}
注解使用时的语法格式:
@注解类型名
//注意:注解可以出现在类上、属性上、方法上、变量上、参数列表上....(注解可以出现在任意位置)
//注解还可以出现在注解类型上
注解中定义属性:
//属性可以使用default指定默认值
//注意:如果一个注解当中有属性,那么必须给属性赋值,除非该属性使用default指定了默认值
//注意:如果属性名是“value”,并且只有一个属性的时候,使用注解时,该属性名可以省略
//*****注解中属性的类型可以是(八大基本类型,String ,Class,枚举类型 ),支持数组
【修饰符列表】 @interface 注解类型名{
类型 属性名();
}
例:public @interface test{
String name();
String color();
int age default 25();//为age设置默认值25,使用时可以忽略age属性
String[] email();//使用了数组,在使用注解时,该属性值需要在大括号{}内
//数组当中如果只有一个元素,大括号是可以省略的
}
使用时:
@test(name=“zhangsan”,color=“blue”,email={“aaa”,“bbb”});
JDK内置注解:
java.lang包下
Deprecated 用@Deprecated注释的程序元素,
@Override:意思是重写父类的方法
//只能注解方法,且只是给编译器参考,跟运行阶段没有关系
//带有该注解,编译器会自动检查
@Deprecated:表示“已过时”
(since:9)//自JDK9之后已过时
元注解:
用来标注“注解类型”的注解,叫做元注解
常用的元注解:
@Target:用来标注“被注解的注解”可以出现在哪些位置上
例:@Target(ElementType.METHOD)//表示“被标注的注解”只能出现在方法上
@Rarget(value={CONSTRUCTOR,FIELD,LOCAL,VARIABLE,METHOD,PACKAGE,MODULE...})
//表示该注解可以出现在:
构造方法上
字段上
局部变量上
方法上
...
类上...
@Retention:用来标注“被注解的注解”最终保存到哪里
@Retention(RetentionPolicy.SOURCE)//表示该注解只被保存在java源文件中
@Retention(RetentionPolicy.CLASS)//表示该注解被保存在Class文件中
@Retention(RetentionPolicy.RUNTIME)//表示该注解被保存在Class文件中,并且可以被反射机制所读取
通过反射机制获取注解:注意@Retention注解属性需要设置为RUNTIME,才可以被反射机制读取到
注解对象的方法:
.value():获取该类上注解中的属性
OCP开闭原则:
对扩展开发,对修改关闭
关于路径的通用型:(*****支持跨操作系统,适合各操作系统,各个环境)
前提是在 ‘类路径’下
什么是“类路径”:在src下的都是类路径(src是类的根路径),但是因为jvm调用的是.class文件,严格意义上来说类路径其实是所有java文件运行之后生成的一个拥有.class文件的目录
*****必须在类的根路径下src
Thread.currentThread().getContextClassLoader().getResource(“从类的根路径下作为起点/文件名.后缀”).getPath()
以上方法获取 “该文件的绝对路径”
Thread.currentThread():返回当前线程对象
getContextClassLoader():线程对象的方法,获取当前线程的类加载器对象
getResource(“从类的根路径下作为起点/文件名.后缀”):类加载器的方法,当前线程的类加载器默认从“类的根路径下”加载资源
getPath():获取路径(获得的是绝对路径)
Thread.currentThread().getContextClassLoader().getResourceAsStream(“从类的根路径下作为起点/文件名.后缀”)
//返回值是一个流,直接以流的形式返回
关于资源绑定器:
//便于获取从“类路径(src)”下作为起点获取.properties文件(属性配置文件)
//注意:只能获取属性配置文件(properties)
//在写路径的时候 文件的扩展名不用写
例:ResourceBundle bundle = ResourceBundle.getBundle("test");
.getBundle(“文件名”):静态方法,参数不可以加文件扩展名,返回一个ResourceBundle对象
.getString(“keyName”):实例方法,参数为属性配置文件key名,返回一个String对象
关于类加载器:
专门负责加载类的命令/工具(ClassLoader)
JDK自带的三个类加载器:
启动类加载器(父加载器):专门加载jdk/jre/lib/rt.jar(rt.jar中都是JDK最核心的类库)
扩展类加载器(母加载器):专门加载jdk/jre/lib/ext/*.jar
应用类加载器:专门加载classpath中的类
//代码开始执行之前,会将所需类加载到JVM中,通过类加载器加载
//首先通过“启动类加载器”,如果找不到会通过“扩展类加载器”加载,再次未找到,最后通过“应用类加载器”加载
java中为了保证类加载的安全,使用了双亲委派机制
关于双亲委派机制:
优先从启动类加载器(父)中加载,“父”无法加载到,再从扩展类加载器(母)中加载。双亲委派,如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止。
关于静态代码块:
在类的成员位置,用static{} 只有在类加载的时候,才会执行,且只执行一次,且由于是在类加载时才会执行,所以甚至先于main方法前执行
static{
}
如果只执行一个类的静态代码块,其他代码一律不执行,可以使用:
Class.forName(“完整类名”)
Java中三大变量:
实例变量:在堆中
静态变量:在方法区中
局部变量:在栈中
(因为线程安全问题的特点是共享数据,而局部变量也就是栈并不共享,所以局部变量不存在线程安全问题)
java根源规则(很重要):
方法体内的代码必须自上而下
return语句一旦执行,方法必须结束
关于反编译工具(重要):很有可能会用到
将class文件反向编译为Java文件,找不到bug时,可以尝试使用反编译工具尝试一下
番外:UML
UML是一种统一建模语言,一种图标式语言。应用于面向对象的语言
常用人员:软件架构师或者系统分析师,软件设计人员。
作用:可以描述类与类之间的关系,程序的执行流程,对象的状态等
UML软件工具
IDEA快捷键:
ctrl+alt+l :代码格式化
alt+left(right):前往左右编辑页
ctrl+d:向下复制一行
ctrl+shift+z:反撤销
alt+F12:查看类的结构
shift+F6:修改变量名或方法名
alt+insert:生成构造器
ctrl+ “+”(“-”):展开或折叠当前方法
ctrl+ shift+“+”(“-”):展开或折叠全部方法
ctrl+alt+F12:打开代码所在硬盘文件夹
ctrl+f:查看当前
ctrl+r:查找/替换
ctrl+h:查看类的继承结构
shift+shift:查找文件
alt+shift+insert:选中指定区域进行操作(批量编辑)
alt+回车键:纠错
IDEA内置模板:
soutv:生成打印语句并输出变量
fori:生成for循环
iter:增强for循环
list.for:生成集合list的for循环
psfs:public static final String