JavaSE知识点总结(力推萌新学习!!!)
磨刀不误砍柴工,下面是我作为初学者,到学完Java基础的感悟。特分享给大家。
在B站官看动力节点视频,最后做出总结,为Java萌新推波助澜,更上一层楼!希望大家在编程的道路上越走越远,愈战愈强!!!。遇到困难,善于分析;解决问题,勤于总结。耐心和坚持是我们非常关键的,大家分享,携手解决遇到的困难,每个人都会得到极大的进步。加油!!!
知识点的最后部分,没有过多的讲解,不是我懒了,而是不想以其昏昏,使其昭昭,把好的学习环境,学习氛围破坏了。最后的时候,希望大家都有一份属于自己的总结。当自己学习其他的内容时,把原来的忘记,温故而知新。所以有一份自己的知识总结,不失为明智之举。在学习我的总结的时候,更多的是希望大家参照我的学习规划来,框架摆在那儿,而内容需要自己去补充。觉得一个知识点需要动手实践多少次,可以灵活运用。编程,是靠实践积累起来的。只看而不屑于做练习,敲代码的人,请止步于此。
强制交易如果大家学到,觉得有帮助的话,请大家要么分享自己的知识总结,要么转发这篇文章,为后面的萌新提供一丝丝便利。另外如果文章的知识点哪里不对,或者有疑义,请大家不吝赐教,发表在评论区,或者留言给我,我及时修改以免更多人被误导。衷心感谢!!!
在此,也动力节点的免费分享学习资料由衷的感谢。
学习视频(万分钟,学到就是赚到!)(https://www.bilibili.com/video/BV1Rx411876f)
1.第一章:Java开发环境的搭建
【1】软件开发介绍
·软件开发
软件:一系列按照特定顺序组织的计算机数据和指令的集合,有系统软件和应用软件之分。
系统软件----操作系统
·人机交互方式
图形化界面(GUI):简单直观、使用者易接受。
命令行方式(CLI):需要有一个控制台,输入特定的指令,需要记住指令,较为麻烦。 DOS
·常用的DOS命令
》dir: 列出当前目录的文件以及文件夹
》md: 创建目录
》rd: 删除目录
》cd: 进入指定目录
》cd…: 退回到上一级目录
》cd: 退回到根目录
》del: 删除文件
》exit: 退出dos命令行
【2】计算机编程语言介绍
计算机语言:人与计算机交流的方式。
C C++ Java PHP Kotlin Python Scala等
机器语言 :指令以二进制代码形式存在。
汇编语言:使用助记符表示一条机器指令。
高级语言:面向过程、面向对象
【3】Java语言
SUN公司1995年推出的一门高级编程语言。
09年Oracle收购。
Java SE 标准版 桌面级应用
Java EE 企业版 企业级应用
Java ME 小型版 移动终端上
Java Card
Java之父 James Gosling
【4】运行机制及运行过程
Java语言特点:
1、面向对象
2、健壮性
3、跨平台性
通过Java语言编写的代码可以在任何操作系统上运行
核心机制--垃圾回收
垃圾回收在Java程序运行过程中自动进行,程序员无法精确控制和干预
【5】Java的环境搭建
JDK(Java Development Kit Java开发工具包)
JRE(Java Runtime Environment Java运行环境)
JDK =JRE+开发工具集(例如javac编译工具等)
JRE=JVM+JavaSE标准类库
安装路径:
不能包含空格和汉字,保留版本号
环境变量:配置路径 系统变量:JDK的bin目录放到path路径下
1、(多个版本JDK)环境变量配置的哪个JDK就用哪个JDK
2、(想配置到用户变量 )新建一个path
3、将新建的path上移到最上面
【6】EditPlus
【7】将写好的代码以 .java 的文件类型保存
cmd Javac 编译 之后 Java 运行
【8】常用的开发工具
2.第二章:Java基础语法
【1】关键字和保留字
·关键字 【图】
·保留字:现有版本尚未使用,以后可能会使用
自己命名时,避开
goto、const
【2】标识符(Identifier)
·标识符:
Java对各种变量、方法和类等要素命名时使用的字符序列
称为标识符。
·技巧:凡是可以自己起名的地方都叫标识符。
·定义合法的标识符规则:
》由26个英文字母大小写,0-9,$或_组成
》数字不可以开头
》不可以使用关键字和保留字、但能包含关键字和保留字。
》Java中严格区分大小写,长度无限制
》标识符不能包含空格。
·Java中的名称命名规范
》包名:多单词组成时所有字母都小写 xxxyyyzzz
》类名、接口名:多单词组成时,所有单词的首字母都大写:XxxYyyZzz
》变量名、方法名:多单词组成时,第一个首字母小写,后边的首字母大写: xxxYyyZzz
》常量名:所有字母都大写、多单词时每个单词用下划线连接 :XXX_YYY_ZZZ
【3】变量
变量怎么定义
变量怎么赋值
变量的作用域
变量的分类
》基本数据类型
12488412 byte short int long float double boolean char
整型 :byte 【1】、short 【2】 、int 【4】、long【8】
浮点型:float 【4】、double【8】
字符型:char 【2】
布尔型:boolean【1】
byte :-128~127
long型变量 以L或l结尾,不加的时候认为是int型
float 定义的变量需要加f
字符编码方式(理解字符编码):DBK,UTF-8,ASCLL ISO-8859-1…
》引用数据类型
类(class)
接口(interface)
数组(array)
》基本数据类型转换
·自动类型提升(容量大小区分)
byte、char、 short -->int long float double
byte char short 运算结果是int
·强制类型转换
1、可能导致精度丢失
2、需要使用强转符
》基本数据类型与String转换
String不是基本数据类型,是引用数据类型
基本数据类型与String + :连接运算。
int Integer.parseInt(str)
double Double.parseDouble(str)
》进制与进制间转换
二进制(binayr) 以0b或者0B开头
八进制(decimal) 以数字0开头表示
十进制 (octal)
十六进制(hex) 0-9以及A-F 以0x或者0X开头
1、标识符的命名规则有哪些?
2、标识符的命名规范有哪些?
3、Java变量按照数据类型怎么划分?并指出Java的基本数据类型有哪8种?
,并指出各自占用的空间大小。
short byte int long float double boolen char
2 1 4 8 4 8 1 2
4、说明基本数据类型变量之间自动类型提升的运算规则。
5、说明基本数据类型变量之间强制类型转换的使用规则和强转可能出现的问题。
1、float 定义变量
2、long 定义变量
【4】运算符
·算术运算符
·+ - * / %
·++(a=2;b=++a; a=3;b=3) ++(a=2;b=a++; a=3;b=2)
·–(a=2;b=–a; a=1;b=1) --(a=2;b=a–;a=1;b=2)
·+(“He”+“llo”) ;“Hello”)
·赋值运算符
· = += -= *= /= %=
·比较运算符(关系运算符)
·== != < > <= >= instanceof(检查是否是类的对象 “Hello” instanceof String 结果 true)
·逻辑运算符
&–逻辑与 |–逻辑或 !–逻辑非
&&–短路与 ||–短路或 ^–逻辑异或
逻辑与:第一个判断合格不合格 后边的代码都要进行判断
短路与:第一个判断不合格 后边的代码不在执行
逻辑异或:判断两个是否是一样的 一样为假 不一样为真
a b a&b a&&b a|b a||b !a a^b
1 1 1 1 1 1 0 0
1 0 0 0 1 1 0 1
0 1 0 0 1 1 1 1
0 0 0 0 0 0 1 0
·位运算符(系统源码会用到,实际开发用到的少)
<< 左移 3<<2=12-->3*2*2=12(在一定范围内)
>> 右移 3>>1=1-->3/2=1(在一定范围内)
>>> 无符号右移 3>>>1=1-->3/2=1
& 与运算 6&3=2
| 或运算 6|3=7
^ 异或运算 6^3=5
~ 取反运算 ~6=-7
左移割去多出来的部分、右移看符号
无符号右移空缺位都用00补
面试题:最高效的方式计算 2*8 2<<3 8<<1
·三元运算符
(条件表达式)?表达式1:表达式2
运算符优先级 想要先用的加括号
【5】流程控制
顺序结构
分支结构
循环结构
控制语句
if
switch
循环语句
for
while
do while
转向语句
break
continue
return
【方法】(函数)
- 方法怎么定义
- 方法怎么调用
- 对方法返回值的理解
- 一个方法怎么返回值的
- 一个方法执行结束之后返回值怎么接收
- 方法的重载overload
·方法递归(递归算法)
3.第三章:面向对象
·面向对象怎么理解?
·类和对象
- 什么是类
- 什么是对象
·类怎么定义
·对象怎么创建
·属性怎么访问
·方法怎么访问
·理解JVM的内存管理
·new出来的对象存储在哪里
·空指针异常
·【封装】:属性私有化,对外提供公开的setter和getter放法。
·构造方法:conatructor
·【this关键字】
1、this是个关键字,翻译成:这个
2、this是一个引用,this是一个变量,this变量保存了内存地址指向了自身,存储在堆内存Java对象内部。
3、每个Java对象都有自身对应的this
4、this可以出现在实例方法当中(this代表当前对象)。实例方法(不带static)
5、多数情况下this可以省略不写
6、this不能使用在带有static的方法中
7、静态方法==>静态变量
8、main方法中没有this
实例变量、实例方法的引用都需要对象。
修饰符列表上有static
采用“类名.“的方式访问,显然这个方法在执行的时候不需要对象的参加。
static的方法调用不需要对象,直接使用类名,所以执行过程中没有当前对象,所以不能使用this
·【static关键字】
1、static关键字可以修饰成员变量和成员方法
- 特点:
被static修饰的成员变量会变成静态变量,静态变量不单单仅仅只是属于某一个对象的,
而是属于类的,所以静态变量会被该类的所有对象共享,也就是说所有该类的对象使用的是同一份数据。
2、实例方法和静态方法的访问
静态方法只能访问静态成员,
实例方法可以访问静态和实例成员。
之所以不允许静态方法访问实例成员变量,
是因为实例成员变量是属于某个对象的,
而静态方法在执行时,并不一定存在对象。
同样,因为实例方法可以访问实例成员变量,
如果允许静态方法调用实例方法,将间接地允许它使用实例成员变量,
所以它也不能调用实例方法。基于同样的道理,静态方法中也不能使用关键字this。
3、static代码块(类构造的时候执行,多个代码块时,按照顺序依次执行)
static关键字还有一个比较关键的作用就是
用来形成静态代码块以优化程序性能。
static块可以置于类中的任何地方,
类中可以有多个static块。
在类初次被加载的时候,
会按照static块的顺序来执行每个static块,
并且只会执行一次。
(JDBC class.format()处有用到静态代码块)
4、Java中规定:static不允许修饰局部变量。
·【super关键字】
1、用法:
- super可以用来引用直接父类的实例变量。
- super可以用来调用直接父类方法。
- super()可以用于调用直接父类构造函数
2、注意:
如果我们没有显式调用super来初始化父类的话,
那么Java会隐式的调用super();
来调用父类无参构造函数并把它放在构造函数的第一行
·【final】
1、final 修饰类,表示这个类不能被继承
final 类中的所有成员方法都会被隐式的指定为final方法
(在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。)
2、final修饰方法
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。
在最近的Java版本中,不需要使用final方法进行这些优化了。“
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
( 注:类的private方法会隐式地被指定为final方法。)
3、修饰变量
修饰变量是final用的最多的地方
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
public static void main(String[] args) {
final int i= 1;
// i =20;
//java: 无法为最终变量i分配值
final Object obj =new Object();
obj =new Object();
//java: 无法为最终变量obj分配值
}
·【继承】
·方法的覆盖
1、什么时候进行方法重写?
如果父类中的方法已经无法满足当前子类的业务需求,需要将父类中的方法进行重新写一遍。就是要改变父类的行为。
注意如果重写父类中的方法之后,子类对象调用的一定是重写之后的方法。
2、发生方法覆盖的条件
- 发生在具有继承关系的两个类之间
- 必须具有相同的方法名,相同的返回值类型,相同的参数列表
- 重写的方法不能比被重写的方法拥有更低的访问权限
- 重写的方法不能比被重写的方法抛出更宽泛的异常
- 私有的方法不能被覆盖
- 构造方法无法被覆盖,因为构造方法无法被继承
- 静态方法不存在覆盖
- 覆盖指的是成员方法,和成员变量无关。
继承最基本的作用是:代码重用。继承最重要的作用:方法可以重写。
【方法重载】
- 方法名称相同
- 方法参数类型、个数、顺序至少有一个不同
- 方法的返回值类型可以不同
- 方法的修饰符可以不同
- 方法重载只能出现同一个类中
·【多态】:多态的基础语法+多态的作用(OCP开闭原则)
1、多态使用的好处:
- 消除了类之间的耦合关系
- 大大提高了程序的简洁性和可拓展性
2、子类对象的多态性,并不适用于属性
3、OCP开闭原则是指一个软件实体如类、模板和函数应该对扩展开放,对修改关闭。
强调的是用抽象构建框架,用实现扩展细节。
开闭原则是面向对象设计中最基础的设计原则。它指导我们应该如何建立稳定灵活的系统。
例如:我们版本更新,尽可能不修改源代码,但是可以增加新功能。
·【抽象类】
1、抽象类是指在普通类的结构里面增加抽象方法的组成部分
2、抽象方法:普通方法上面会有{},这个表示方法体,有方法体的方法一定可以被对象直接调用,而抽象方法是指没有方法体的方法,
同时抽象方法还必须使用关键字abstract做修饰。
而拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
3、抽象方法的使用原则如下:
- 抽象方法必须为public 或者protected(因为如果为private,则不能被子类继承,子类边无法实现该方法),缺省情况下默认为public
- 抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理
- 抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类
- 子类(如果不是抽象类)则必须覆盖抽象类中的所有抽象方法。(如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract类)
4、抽象类的使用限制
(1)抽象类中有构造方法么?
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
(2)抽象类可以用final声明么?
不能,因为抽象类必须有子类,而final定义的类不能有子类;
(3)抽象类能否使用static声明?
外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
·【接口】
- 接口的定义:接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
- 接口的特点:
1、Java接口中的成员变量默认都是public 、static、final类型的(都可以省略),必须被显示初始化,即接口中的成员变量为常量。
2、Java接口的方法默认都是public、abstract类型的(都可省略),没有方法体,不能被实例化
3、Java接口中的方法只能包含public static final 类型的成员变量和public abstract 类型的成员方法
4、接口中没有构造方法,不能被实例化
5、一个接口不能实现(implement)另一个接口,但是它可以继承多个其他接口
6、Java接口必须通过类来实现它的抽象方法
7、当类实现某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
8、不允许创建接口的实例,但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例
9、一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。
- 接口的用法
两个及以上的类拥有相同的方法,但是实现功能不一样,就可以定义一个接口,将这个方法提炼出来,在需要使用该方法的类中去实现,就免去了定义系统方法的麻烦。
·例如:人会吃饭,睡觉
人有三个身份 学生、 教师 、校领导
吃的不一样,住的不一样
但是都会吃、住。
此时定义一个接口
interface Peron{
void eat();
void sleep();
}
·【Object类中的常用方法】
equals hashCode toString finalize clone..
·【内部类】:重点掌握匿名内部类。
内部类:
1.什么是内部类?
内部类:在类的内部又定义了一个新的类。被称为内部类。
2.内部类的分类
静态内部类:类似于静态变量
实例内部类:类似于实例变量
局部内部类:类似于局部变量
3.匿名内部类
new 接口{实现方法}
·【向上转型 ||向下转型】
- 向上转型
通过子类对象(小范围)实例化父类对象(大范围),这种属于自动转换
向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.
public class A{
public void fun(){
sout("A");
}
}
public class B extends B{
public void fun(){
sout("B");
}
}
main(){
A a= new B(); //向上转型 自动的
a.fun();
}
输出: B
- 向下转型
通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换
public class C extends A{
public void fun() {
System.out.println(“C”);
}
public void funC(){
System.out.println("C的方法啊");
}
}
public class ABCtest {
public static void func(A a){
a.fun();
if( a instanceof B){
B b =new B();
b.funB();
}
else if(a instanceof C){
C c= new C();
c.funC();
}
}
public static void main(String[] args) {
func(new A());
func(new B());
func(new C());
}
}
结果:A
B
B的方法
C
C的方法啊
【JVM内存结构——堆、栈、方法区】
方法区;类加载器classloader,将硬盘上的xxx.class字节码文件装载到JVM的时候,会将字节码文件存放到方法区当中,也就是说方法区中存储的是代码片段。
(因为类需要加载,所以方法区当中最先有数据。)
栈内存:在方法被调用的时候,该方法需要的内存空间在栈中分配。
方法只有在调用的时候才会在栈中分配空间,并且调用的时候就是压栈。
方法执行结束之后,该方法所需要的空间就会释放,此时发生弹栈动作。
(给main方法分配空间)
一、定义
1、堆:FIFO队列优先,先进先出。jvm只有一个堆区被所有线程所共享!堆存放在二级缓存中,调用对象的速度相对慢一些,生命周期由虚拟机的垃圾回收机制定。
2、栈:FILO先进后出,暂存数据的地方。每个线程都包含一个栈区!栈存放在一级缓存中,存取速度较快,“栈是限定仅在表头进行插入和删除操作的线性表”。
3、方法区:用来存放方法和static变量。
二、存储的数据类型
1、堆用来存储new出来的对象和数组
2、栈用来存储基本类型变量和对象的引用变量的地址
3、方法区存储方法和static变量
三、优缺点
1、堆的优点-可以动态的分配内存大小,生命周期不确定。缺点-速度略慢
2、栈的优点-速度快,缺点-存在栈中的数据大小和生命周期必须是明确的,缺少灵活性。
4.第四章:数组
数组的内存解析
栈 堆 方法区
局部变量 new出来的结构 常量池
(对象、数组) 静态域
1、写出一维数组初始化的两种方式。
int[] arr=new int[5];//动态初始化
int[] arr=new int[]{};//静态初始化
2、写出二维数组初始化的两种方式。
int[][] arr=new int[len][len];//动态初始化1
int[][] arr=new int[len][];//动态初始化2
int[][] arr=new int[][]{
{},{}};//静态初始化
3、如何遍历如下的二维数组
int[][] arr=new int[][]{
{1,2,3},{6,7,8}};
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]);
}
System.out.print("\n");
}
4、不同类型的一维数组元素的默认初始化值各是多少?
整型:0
浮点型:0.0
char :0
boolean:false
引用类型:null
5、一维数组的内存解析。
*数组一旦初始化,其长度就是确定的,不能更改。
【Arrays工具类的使用】
查看帮助文档。
常用方法
/*
* boolean equals(int[] a,int[] b) 判断两个数组是否相等
* String toString(int[] a) 输出数组的信息
* void fill(int[]a ,int val) 将指定值填充到数组之中
* void sort(int[] a) 对数组进行排序
* int binarySearch(int[] a,int key) 对排序后的数组进行二分检索指定的值
* *
常见异常
/*
*数组角标越界异常:ArrayIndexOutOfBoundsException
*空指针异常:NullPointerException
**/
【String[] args】
当你使用命令行bai的形du式运行.class文件时,向你的类传递参数zhi.
C:\>java YourClassName 参数1 [参数2 参数3 ....]参数之间用一个空格bai作为间隔符.
String[] args 这个字符串数组是保存运行main函数时输入的参数的
,例如main函数所在的类名为test
那么你在cmd运行 java test a b c 时,
args[0] = a ,args[1]=b, args[2]=c
你就可以在你的程序中调用你输入的这些变量了。
【数组的扩容】
① int[] arr2=new int[arr1.length*2] //新数组长度
for(int i=0;i<arr1.length;i++){ //复制
arr2[i]=arr1[i];
}
② int[] arr2=java.util.Arrays.copyOf(原数组名,新数组长度);
③ int[] arr2=new int[arr1.length*2]
System.arraycopy(原数组名,起始下标,新数组名,起始下标,复制长度);
(本质就是用一个新的容量大的数组拷贝原数组的值)
1、使用冒泡排序,实现如下的数组从小到大排序
int[] arr=new int[]{34,5,22,-98,6,-76,0,-3};
2、如何反转上面的数组,请代码实现
3、复制上述数组,得到一个新的数组
4、使用线性查找,从上述数组中查找22是否存在,存在返回所在位置
的索引,不存在,输出提示信息
5、数组中常见的异常有哪些?请举例说明。
5.第五章 异常
·异常怎么理解
程序执行过程中出现了不正常情况,这种不正常情况叫做,异常。
java语言是很完善的语言,提供了异常的处理方式,一下程序执行过程中出现了不正常的情况,
Java把该异常信息打印输出在控制台,供程序员参考,程序员看到异常信息在之后,可以对
程序进行参数修改,让程序更加的健壮。
- 异常信息是JVM打印的。
·异常在Java中有什么用?
- 给程序员提示程序错误信息,一边修改。
- 增强健壮性
·异常在Java中以什么形式存在?
- 异常以类和对象的形式存在
public static void main(String[] args) {
NullPointerException n =new NullPointerException(“空指针异常”);
System.out.println(n);
//java.lang.NullPointerException: 空指针异常
}
·异常的继承结构图
Throwable(父类)
ERROR(虚拟机错误、内存溢出、线程死锁) Exception(RuntimeException、IO异常、SQL异常)
RuntimeException(空指针异常NullPointerException、
数组下标越界异常 ArrayIndexOutOfBoundsException、
算数异常ArithmeticException、
类型转换异常ClassCastException)
·什么是编译时异常
是RuntimeException以外的异常,
类型上都属于Exception类及其子类。
从程序语法角度讲是必须进行处理的异常,
如果不处理,程序就不能编译通过。
如IOException、SQLException等以及用户自定义的Exception异常,
一般情况下不自定义检查异常。
·什么是运行时异常
都是RuntimeException类及其子类异常,
如NullPointerException(空指针异常)、
IndexOutOfBoundsException(下标越界异常)等,
这些异常是不检查异常,程序中可以选择捕获处理,
也可以不处理。这些异常一般是由程序逻辑错误引起的,
程序应该从逻辑角度尽可能避免这类异常的发生。
运行时异常的特点是Java编译器不会检查它,也就是说,
当程序中可能出现这类异常,即使没有用try-catch语句捕获它,
也没有用throws子句声明抛出它,也会编译通过。
·ERROR (错误)是程序无法处理的错误。表示代码运行时JVM出现问题。如JVM运行错误、类定义错误。
·异常的两种处理方式:
第一种:捕捉try catch
输出打印异常信息,
第二种:throws(声明该方法出现此类异常时上抛)
调用方法时必须对这种异常进行预先的处理,如果不处理就会报错。
*未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
·finally子句的用法
- 在finally子句中的代码是最后执行的,并且是一定执行的,即使是try语句中的代码出现异常
必须和try一起出现。不能单独编写
- finally语句通常在哪些情况下执行?
流使用完要关闭,占用资源的关闭时,在finally子句中关闭。
·怎么自定义异常
两步:
第一步:编写一个类继承Exception(编译时异常)或者RuntimeException(运行时异常)
第二步:提供两个构造方法,一个无参数的,一个带有String 参数的
·怎么手动抛异? throw new RuntimeException
·异常对象的两个方法?
e.getMessage();获取异常的简短信息
e.printStackTrace();打印异常追踪堆栈信息
·方法重写与异常
·关键字:
try
catch
finally
throw
throws
- try catch
1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型
2、catch可以写多个,建议catch的时候,精确的一个一个处理,这样有利于程序的调试
3、catch写多个的时候,从上到下,必须遵循从小到大的原则
【上报和捕捉】
希望调用者知道就上报,希望程序继续运行就捕捉。
【JDK8的新特性】 :catch(异常1|异常2)
【final、finally、finalize】
final是一个关键字 表示最终的、不变的
finally也是一个关键字
finalize 是一个标识符。是一个方法。
6.第六章 常用类
·String类的实现原理,内存存储方式。
- String str = “abc”;
相当于; char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
- 存储在方法区的常量池中
双引号创建的字符串可以直接用,new出来的字符串当对象处理
·String一旦创建就不会改变,如果有+ 那么实际新建三个字符串
-字符串的比较:s1.equals(s2);
·String类的构造方法
- String s = new String(“abc”);
- String s =“abc”;
·String类的常用方法
- 见JDKAPI帮助文档
·StringBuilder StringBuffer的常用方法?以及和String的区别
- java字符串是不可变的,开发过程中,需要大量的字符串拼接,会占用大量的方法区内存,造成内存空间的浪费。
StringBuffer 进行字符串拼接。
- 区别
StringBuilder:初始容量16(线程不安全)
StringBuffer:初始容量16 (线程安全的synchronized修饰表示StringBuffer 在多线程环境运行是安全的)
·八种基本数据类型对应的包装类
基本数据类型 包装类型
--------------------------
byte java.long.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
boolean java.lang.Boolean
char java.lang.Character
Integer为代表
自动装箱和自动拆箱是什么?
-JDK 1.5之后支持自动拆箱和自动装箱
·自动装箱:基本数据类型自动装换成包装类
·自动拆箱:包装类自动转换成基本数据类型
·有了自动拆箱之后,Number类中的方法就用不着了;
Integer、int、String三种类型互相转换
·数字的处理
NumberFormat
BigDecimal(财务数据)
·日期处理
怎么获取系统当前时间
Date date =new Date();
怎么获取毫秒
date.getTime();
怎么进行日期格式化
怎么将字符串日期转换成日期类型Date
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-mm-dd hh:MM:ss SSS");
Date date= sdf.parse("String");
·枚举类型
返回值两种 Boolean
返回值多种 使用enum枚举类型
public class enumTest {
public static void main(String[] args) {
Serson s =fun();
System.out.println(s);
}
public static Serson fun(){
return Serson.SPRING;
}
}
enum Serson{
SPRING,SUMMER,AUTOMU,WINTER
}
·随机数
import java.util.Random;
Random random =new Random();
int i =random.nextInt(10);
生成0-1之间的随机数。
double random = Math.random();
7.第七章 集合
·集合概述
1.1什么是集合?有什么用?
数组其实就是个集合,集合实际上就是一个容器,可以容纳其他类型的数据。
集合为什么说在开发中使用较多?
集合是一个容器,一个载体,可以一次容纳多个对象。
·集合中都可以存储什么元素
集合不能直接存储基本数据类型,另外集合也不能直接存储Java对象,
集合当中存储的都是Java对象的内存地址,或者说集合中存储的是引用
注意:
集合在Java中本身是一个容器
集合在任何时候存储的都是"引用"
·在Java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;
一类是以键值对的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;
·集合的继承结构图Collection
·集合的继承结构图Map
·Collection接口中常用的方法
见帮助文档
·Collection集合的遍历方法
~Iterator it =Collection.Iterator();
it是迭代器对象
遍历集合
while(it.hasNext()){
Object o = it.next();
System.out.println(o);
}
·List集合特点
List集合中特有的方法
~get() ~indexOf() ~set() ~remove() ~lastIndexOf()
·Set集合特点
·ArrayAList集合
底层是什么数据结构
ArrayList集合的初始化容量
ArrayList集合的扩容是怎样的?
(01) ArrayList 实际上是通过一个数组去保存数据的。当我们构造ArrayList时;若使用默认构造函数,则ArrayList的默认容量大小是10。
(02) 当ArrayList容量不足以容纳全部元素时,ArrayList会重新设置容量:新的容量=“(原始容量x3)/2 + 1”。
(03) ArrayList的克隆函数,即是将全部元素克隆到一个数组中。
(04) ArrayList实现java.io.Serializable的方式。当写入到输出流时,先写入“容量”,再依次写入“每一个元素”;当读出输入流时,先读取“容量”,再依次读取“每一个元素”。
·LinkedList集合
底层是什么数据结构
双向链表
链表数据结构的优点和缺点
优点:在指定位置增加删除元素效率高
缺点:查找效率低
·Vectot集合
底层是什么数据结构
数组
Vector集合的初始化容量
Vector集合的扩容是怎样的
Vector和ArrayList集合的区别
synchronized!!!
·HashSet集合底层是HashMap
·SortedSet接口,可排序的集合,子类有:TreeSet
·TreeSet集合底层是TreeMap集合
·Map集合中常用的方法
·HashMap集合的底层数据结构
哈希表
哈希算法是怎样的
为什么要重写hashcode equals
HashMap的默认加载因子
HashMap集合的初始化容量
Hash Map的扩容是怎样的
借鉴文章:
https://blog.csdn.net/zhengwangzw/article/details/104889549?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight
·Hash table集合底层是什么数据结构
·Hashtable下的子类Properties有一定的理解
·Sorted Map的key可排序是如何实现的
·TreeMap集合底层是一个二叉树的数据结构,他是怎么样的一个数据结构
·Map集合的遍历有多种方式需要掌握
public class Test4 {
public static void main(String[] args) {
HashMap<Phone,String> map = new HashMap<>();
map.put(new Phone(“Apple”,7000),“美国”);
map.put(new Phone(“Sony”,5000),“日本”);
map.put(new Phone(“Huawei”,6000),“中国”);
Set<Map.Entry<Phone, String>> entries = map.entrySet();
for (Map.Entry<Phone, String> entry : entries) {
System.out.println(entry.getKey().getBrand()+"="+entry.getKey().getPrice()+"="+entry.getValue());
}
}
}
·集合的泛型
泛型标志:<>
·集合的工具类:Collection的常用方法
总结:(所有实现类)
ArrayList:底层是数组
LinkedList:底层是双向链表
Vector:底层是数组,线程安全的,效率较低,使用较少
HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合的key部分了
TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到Tree Map集合key部分了
HashMap:底层是哈希表,之不够线程安全的,效率较低,使用较少
Properties:是线程安全的,并且key和value只能存储字符串
TreeMap:底层是二叉树,TreeMap集合的key可以自动按照大小顺序排序
List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出的顺序相同,每一个元素都有下标
可重复:有一个1 还可以在存1
Set集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出的顺序不一定相同,另外Set集合元素没有下标
不可重复:有1 了不能在存储1了
Sorted(SortedMap)集合存储元素的特点:
首先是无序不可重复,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出的顺序不一定相同,另外Set集合中元素没有下标
可排序:可以按照大小顺序排序
Map集合的key,就是一个Set集合
在Set集合中放数据,实际上是放到了Map集合的key部分。
8.第八章 IO流(I:Input O:Output)
skip() 跳过并从输入流中丢弃 n字节的数据。
available()返回从此输入流中可以读取(或跳过)的剩余字节数的估计值,而不会被下一次调用此输入流的方法阻塞。
~重点掌握哪些流?16个流
文件专属:
FileInputStream
FileOutputStream
FileReader
FileWriter
缓冲流专属:
BufferedReader
BufferedWriter
BufferdInputStream
BufferdOutputStream
转换流:(将字节流转换成字符流)
InputStreamReader
OutputStreamWriter
对象专属流:
ObjectInputStream
ObjectOutputStream
数据流专属:
DataInputStream
DaraOutputStream
标准输出流:
PrintWriter
PrintStream
~流的分类?(在Java中只要"类名"以Stream结尾的都是字节流,以"Reader/Writer"结尾的都是字符流)
输入流 输出流
字节流 字符流(四大家族的首领都是抽象类)
节点流 包装流
(当一个流的构造是一个流的时候,构造里的这个流叫做节点流,外部的流叫做包装流)。
一种方式是按照流的方向进行分类:
以内存作为参照物,
往内存中去,叫做输入(Input),或者叫做读(Read)
从内存中出来,叫做输出(Output),或者叫做写(Write)
另一种方式是按照读取数据方式不同进行分类:
有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位,这种流是方能的,
什么类型的文件都可以读取,例如:文本文件,图片,声音文件,视频
有的流是按照字符的方式读取数据的,一次读取一个字符,这种流为了方便读取文本文件而存在的,这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,
连word都不无法读取。
所有的流都实现了:
java.io.Closeable接口,都是可关闭的。
流毕竟是一个管道,是内存和硬盘之间的通道,
会耗用资源,用完一定要关闭。
所有的输出流都实现了:
java.io.Flushable接口,都是可刷新的,都有flush()方法。
养成了一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下
。这个刷新表示将管道当中剩余未输出的数据强行输出完,刷新的作用:清空管道
注意:如果没有flush()可能会缺失数据
~能够使用流完成文件的复制
~掌握File类的常用方法。
1.File类和四大家族没有关系,所以不能完成文件的读和写。
2.File对象代表什么?
文件和目录路径名的抽象表示形式。
C:\Drivers 这是一个File对象
C:\Drivers\readMe.txt 这也是File对象
一个File对象有可能对应的是目录,也可能是文件。
File只是一个路径名的抽象表示形式。
3.常用方法:
~见文件夹 File类常用方法
FileInputStream:
/*
-
1.文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
-
2.字节的方式,完成输入的操作,完成读的操作(硬盘—>内存)
-
-
*/
BufferedReader:
1.带有缓冲区的字符输入流
2.使用这个流的时候不需要自定义char数据,自带缓冲。DataInputStream
1.写的文件记事本打不开,以什么顺序写入的,就以什么顺序读取。PrintStream
1.标准的字节输出流,默认输出到控制台的
2.标准的输出流不需要close()关闭
/* -
标准的输出流(日志框架原理)
-
*/
public class PrintStreamTest {
public static void main(String[] args)throws Exception {
PrintStream ps= new PrintStream(new FileOutputStream(“D:\从零开始\java\IO流\PrintStreamFile.txt”));
//改变输出方向
System.setOut(ps);ps.println("hello"); ps.println("你好"); ps.println("My Girl");
}
}
ObjectOutStream类要实现Serializable接口(JVM自动生成序列化版本号)
建议:手动写序列化版本号
private static final long serivalVersionUID=???
序列化和反序列化
序列化多个对象:将对像放到List集合中,将集合序列化、反序列化
transient关键字:修饰属性时不参与序列化
Java语言中采用什么机制区分类的?
第一:首先通过名字进行比对,如果类名不一样,肯定不似乎同一类
第二:如果类名一样,再怎么进行类的区别,靠序列化版本号进行区分。
自动生成的序列化版本号有什么缺陷?
后期不能修改代码最终结论: 凡是类实现了Serializable,给类一个固定不变的版本号。
IO和Properties联合使用
/* -
IO 和Properties 联合使用
-
*/
import java.io.FileReader;
import java.util.Properties;
public class IOTest {
public static void main(String[] args) throws Exception{
FileReader fr =new FileReader(“D:\从零开始\java\IO流\IOProperties.txt”);
Properties p =new Properties();
p.load(fr);
String username = p.getProperty(“username”);
System.out.println(username);
fr.close();
}
}
9.第九章 多线程
1.什么是进程?什么是线程?
进程是一个应用程序,(一个进程是一个软件)
线程是一个进程的执行场景/执行单元
一个进程可以启动多个线程
2.对于Java程序来说,当在DOS命令窗口输入:
Java helloWorld回车之后,
会先启动JVM,而JVM就是一个进程
JVM在启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾。
最起码Java程序中至少有两个线程并发,一个是垃圾回收线程,一个是执行main方法的主线程
3.进程和线程是什么关系?
进程A和进程B的内存独立不共享
线程A和线程B
在Java语言中,堆内存和方法区内存共享,
但是栈内存独立,一个线程一个栈。
假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,互不干扰。
Java之所以有多线程机制,目的就是提高程序的处理效率。
4.使用多线程机制之后,main方法结束,是不是有可能程序没结束
5.对于单核的CPU来说,真的可以做到真正的多线程并发吗?
什么是真正的对线程并发?
t1线程执行t1的。
t2线程执行t2的。
t1不会影响t2,t2也不会影响t1,这叫真正的多项成并发。
单核CPU表示只有一个大脑,
不能够做到真正的多项成并发,但是可以做到给人一种"多线程并发"的感觉。
对于单核CPU来书,在某一个时间点只能处理一件事请,但是由于CPU的处理速度极快。
多个线程之间频繁切换执行,给人感觉是多个事请同时在做。
6.Java语言中,实现多线程有两种方式,哪两种方式呢?
第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
//定义线程类
public class MyThread extends Thread{
public void run(){
}
}
//创建线程对象
MyThread t =new MyThread();
//启动线程
t.start();
第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
//定义一个可运行的类
public class MyRunnable implements Runnable{
public void run(){
}
}
//创建线程对象
Thread t =new Thread(new MyRunnable());
//启动线程
t.start();
============================================================
//匿名内部类
Thread t =new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(“分支线程”+i);
}
}
});
t.start();
~多线程的三种方式
继承Thread
实现Runable
实现Callable
~多线程的生命周期
~线程的调度(sleep、yield...)
~线程的安全(重点)
~定时器/定时任务
~守护线程
~wait方法和notify方法,生产者和消费者模式
24小时多线程笔记:
1 线程概述
1.1 线程相关概念
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,
是操作系统进行资源分配与调度的基本单位。
可以把进程简单的理解为正在操作系统中运行的一个程序。
线程
线程(thread)是进程的一个执行单元。
一个线程就是进程中一个单一顺序的控制流,进程的一个执行分支。
进程是线程的容器,一个进程至少有一个线程,一个进程也可以有多个线程。
在操作系统中是以进程为单位分配资源,如虚拟存储空间,文件描述符等。
每个线程都有各自的线程栈,自己的寄存器环境,自己的线程本地存储。
主线程与子线程
JVM启动时会创建一个主线程,该主线程负责执行main方法,主线程就是运行main
方法的线程
Java中的线程不孤立,线程之间存在一些联系,如果在A线程中创建B线程,称B线程为A线程的子线程,相应的A线程就是B
线程的父线程。
串行,并发与并行
串行(Sequential)先做任务A,完成之后在做任务B,完成之后再做任务C
,所有的任务逐个完成。
并发(Concurrent)先开始做任务A,在等待A完成的这段时间内开始做任务B,在等待B完成的时候做任务C.任务交替完成的。
并行(Parallel),三个任务同时开始。总耗时取决于需要时间最长的那个任务。
并发可以提高事物的处理效率,即在一段时间内可以处理或者完成过呢更多的事请。
并行是一种更为严格,理想的并发。
从硬件角度来说,如果单核CPU,一个处理器一次只能执行一个线程的情况下,处理器可以使用时间片轮转技术,可以让CPU快速的在各个
线程之间切换,对于用户来说,感觉三个线程在同时执行,如果是多核CPU,可以为不同的线程分配不同的内核。
1.2 线程的创建与启动
在Java中创建一个线程就是创建一个Thread类(子类)的对象(实例)。
Thread类有两个常用的构造方法:Thread()与Thread(Runnabled)对应的创建线程的两种方式。
定义Thread类的子类
定义一个Runnable接口的实现类
这两种创建线程的方式没有本质的区别。
1.3 线程的常用方法
1.currentThread()方法
Thread.currentThread()方法可以获得当前线程
Java中的任何一段代码都是执行在某个线程当中的,执行当前代码的线程就是当前线程。
同一段代码可能被不同的线程执行,因此当前线程是相对的,这个方法的返回值是在代码实际运行的
时候线程对象,
2.setName()/getName()
thread.setName(线程名称),设置线程名称
thread.getName()返回线程名称
通过设置线程名称,有助于程序调试,提高程序的可读性,建议为每个线程都
设置一个能够体现线程功能的名称。
3.isAlive()
thread.isAlive()判断当前线程是否处于活动状态
活动状态就是线程已启动并且尚未终止。
4.sleep()
Thread.sleep(毫秒数):当前线程睡眠多少毫秒
当前线程指的是:Thread.currentThread()返回的线程
注意:子线程的run()方法中异常只能捕获处理,而不能上抛,因为父类的run方法没有抛出异常。
5.getId()
thread.getId():可以获得线程的唯一标识
注意:
某个编号的线程运行结束后,该编号可能被后续创建的线程使用
重启的JVM后,同一个线程的编号可能不一样
6.yield()
Thread.yield()方法的作用是放弃当前的CPU资源,
线程让步,放弃CPU的执行权。
7.setPriority()
thread.setPriority(num);设置线程的优先级
Java线程的优先级取值范围是1~10,如果超出这个范围会抛异常IllegalArgumentException.
在操作系统中,优先级较高的线程获得CPU的资源越多
线程优先级本质上只是给线程调度器一个提示信息,以便与调度器决定先调度哪些线程,注意不能保证优先级高的线程先运行。
Java优先级设置不当或者滥用可能会导致某些线程永远无法得到运行,即产生了线程饥饿。
线程的优先级不是设置的越高越好,一般情况下使用普通的优先级即可,即在开发时不必设置线程的优先级。
8.interrupt()
中断线程.
注意调用interrrupt()方法仅仅是在当前线程打一个停止标志,并不是真正的停止线程。
isInterrupted()
该方法可以返回线程的中断标志。
9.setDaemon()
java中的线程分为用户线程与守护线程
守护线程是为其他线程提供服务的线程,如垃圾回收器(GC)就是一个典型的守护线程。
守护线程 不能单独运行,当JVM中没有其他用户线程,只有守护线程时,守护线程会自动销毁,JVM会退出。
注意:设置守护线程应该在start()之前。
1.4 线程的生命周期
线程的生命周期是线程对象的生老病死,即线程的状态。
线程生命周期可以通过getState()方法获得,线程的状态是
Thread.State 枚举类型定义的,由以下几种:
NEW ,新建状态,创建了线程对象,在调用start()启动之前的状态;
RUNNABLE,可运行状态。它是一个复合状态,包含:READY 和RUNNING两个
状态,READY状态该线程可以被线程调度器进行调度使它处于RUNNING状态,RUNNING状态表示该线程正在执行。
Thread.yield()方法可以把线程由RUNNING状态转换为READY状态。
BLOCKED,阻塞状态,线程发起阻塞的I/O操作,或者申请由其他线程占用的独占资源,线程会转为BLOCKED阻塞状态,
处于阻塞状态的线程不会占用CPU资源。当阻塞I/O执行完,或者线程获得了其申请的资源,线程可以转换为RUNNABLE。
WAITING,等待状态。线程执行了object.wait(),thread.join()方法会把线程转换为WAITING等待状态,执行object,notify()方法,
或者加入的线程执行完毕,当前线程会转换为RUNNABLE状态。
TIMED_WAITING,状态与WAITING状态类似,都是等待状态,区别在于处于该状态的线程不会无限的等待,如果线程没有在指定的时间范围内完成期望的操作,
该线程自动转换为RUNNABLE
TERMINATED,终止状态,线程结束处于终止状态。
1.5多线程编程的优势与存储的风险
多线程编程具有以下优势:
1)提高系统的吞吐率(Throughout)。多线程编程可以使一个进程有
多个并发(concurrent),即同时进行的操作。
2)提高响应性(Responsiveness)。Web服务器会采用一些专门的线程
负责用户的请求处理,缩短了用户的等待时间
3)充分利用多核(Multicore)处理器资源,通过多线程可以充分的利用CPU资源
多线程编程存在的问题与风险:
1)线程安全(Thread safe)问题。多线程共享数据时,如果没有采取争取的
并发访问控制措施,就会产生数据一致性问题,如读取脏数据(过期的数据)
如丢失数据更新。
2)线程活性(Thread liveness)问题。由于程序自身的缺陷或者有资源稀缺性导致线程
线程一直处于非RUNNABLE状态,这就是线程活性问题,常见的活性故障有以下几种:
(1)死锁(Deadlock),类似于鹬蚌相争。
(2)锁死(Lockout),类似于睡美人 故事中王子挂了,
(3)活锁(Livelock),类似于小猫咬自己尾巴
(4)饥饿(Starvation),类似于健壮的雏鸟总是从母鸟嘴中抢到食物
3)上下文替换(Context Switch)处理器从执行一个线程切换到执行另外一个线程
4)可靠性 可能会有由一个线程导致JVM意外终止,其他的线程也无法执行
2 线程安全问题
非线程安全主要是指多个线程对同一个对象的实例变量进行操作时,
会出现值被更改,值不同步的情况。
线程安全问题表现为三个方面:原子性、可见性和有序性
2.1原子性
原子(Atomic)就是不可分割的意思。原子操作的不可分割有两层含义:
1)访问(读、写)某个共享变量的操作从其他线程来看,该操作要么已经执行完毕
,要么尚未发生,其他线程看不到当前操作的中间结果。
2)访问同一组共享变量的原子操作是不能够交错的,
Java中有两种方式实现原子性:一种是使用锁;另一种利用处理器的CAS(Compare And Swap)指令
锁具有排他性,保证共享变量在某一时刻只能被一个线程访问,
CAS指令直接在硬件(处理器和内存)层次上实现,看作是硬件锁。
2.2可见性
在多线程环境中,一个线程对某个共享变量进行更新之后,后续其他的线程可能无法立即读到这个更新的结果,这就是线程安全问题的
的另一种形式:可见性(visibility)
如果一个线程对共享变量更新后,后续访问该变量的其他线程可以读到更新的结果,称这个线程对共享变量的更新对其他线程可见,否则称这个
线程对共享变量的更新对其他线程不可见。
多线程程序因为可见性问题可能会导致其他线程读到了旧数据(脏数据)。
2.3有序性
有序性(Ordering)是指在什么情况下一个处理器上运行的一个线程所执行的,内存访问操作在另外一个处理器运行的其他线程看来是乱序的(Out of Order).
乱序是指内存访问操作的顺序看起来发生了变化
在多核处理器的环境下,编写的顺序结构,这种操作执行的顺序可能是没有保障的;
编译器可能会改变两个操作的先后顺序;
处理器也可能不会按照目标代码的顺序执行;
这种一个处理器上执行的多个操作,在其他处理器来看它的顺序于目标代码指定的顺序可能不一样,
这种现象称为重排序。
重排序是对内存访问有序操作的一种优化,可以在不影响单线程的程序正确的情况下提升程序的性能,但是可能对多线程程序的
正确性产生影响,即可能导致线程安全问题。
重排序与可见性问题类似,不是必然出现的。
与内存操作顺序有关的几个概念:
源代码顺序,就是源码中指定的内存访问顺序。
程序顺序,处理器上运行的目标代码所指定的内存访问顺序
执行顺序,内存访问操作在处理器上的实际执行顺序。
感知顺序,给定处理器所感知到的该处理器及其他处理器的内存访问操作的顺序
2.4Java内存模型
10.第十章 反射机制
1.反射机制有什么用?
用过Java语言中的反射机制可以操作字节码文件。
可以读和修改字节码文件
通过反射机制可以操作代码片段(class文件)
2.反射机制相关的类有哪些?
java.lang.Class :代表整个字节码,代表一个类型
java.lang.reflect.Method:代表字节码中的方法字节码
java.lang.reflect.Constructor:代表字节码中的构造方法字节码
java.lang.reflect.Field:代表字节码中的属性字节码。
//java.lang.class
public class User{
//Field
int no;
//Constructor
puclic User(){
}
//method
public void setNo(int no){
this.no=no;
}
public int getNo(){
return no;
}
}
~获取Class的三种方式
第一种方式:
Class.forName()
1.静态方法
2.方法的参数是一个字符串/
3.字符串需要的是一个完整类名
4.完整类名必须带有包名。java.lang包也不能省略。
例:
try{
Class c1 =Class.forName("java.lang.String"); //c1代表String.class文件
}catch{
e.printStackTrace();
}
第二种方式:
Java中任何一个对象都有一个方法getClass();
例:
String s= "abc";
Class x =s.getClass();//x代表String.class字节码文件
第三种方式:
Java语言种任何一种类型,包括基本数据类型,它都有.class 属性
例:
Class z =String.class;
Class x =Date.class;
~通过反射机制能够实例化对象
反射机制的灵活性:
一次代码,再不改变原来的代码基础上可以做到不同对象的实例化。
符合OCP原则(对扩展开放,对修改关闭)
重点:
如果只是希望一个类的静态代码块执行,其他代码不执行,
可以使用:
Class.ForName("完整类名");
(JDBC 处使用)
~路径:
src是类的根路径。
获取路径(采用这种方式可以拿到一个文件的绝对路径)
记住: String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
~资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
属性配置文件xxxx.properties放在类路径下。
ResourceBundle bundle =ResourceBundle.getBundle("文件名不带后缀");
String className =bundle.getString("className");
~关于JKD自带的类加载器
1.什么是类加载器?
专门负责加载类的命令/工具
ClassLoader
2.JDK中自带了三个类加载器
启动类加载器
扩展类加载器
应用类加载器
3.假设有这样一段代码
String s="abc";
代码在开始执行之前,会将所需要类全部加载到JVM中,
通过类加载器加载,看到以上代码类加载器会找String.class
文件,找到就加载,那么是怎么进行加载的呢?
首先通过"启动类加载器"加载
注意:启动类加载器专门加载rt.jar核心的类库
如果找不到,那么扩展器扩展类
扩展器没有找到,通过应用类加载器加载
~反射Filed,操作Field
~反射Method,操作Method
~反射Constructor,操作Constructor
~通过反射机制获取父类,以及获取父接口
~可变长参数:
int...args
/*可变长度参数
-
1.可变长度参数要求是类型…(一定是三个…)
-
2.参数个数0-N个
-
3.可变长度参数一定在最后位置,并且只能由一个
-
*/
public class args {
public static void main(String[] args) {
fun(3);
fun(10,30);
fun(1,2,3,4);
m1(“hello”);
m1(“zhangsan”,“nihao”);
m1(“a”,“b”,“c”,“d”);}
public static void fun(int …args){
System.out.println(“fun方法执行了”);
}
public static void m1(String …args){
//args属性有length说明是一个数组 也可以传一个数组
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
11.第十一章 注解
~怎么自定义注解?
1.注解,或者叫做注释类型,英文单词是:Annotation
2.注解Annotation是一种引用数据类型。编译之后也是生成xxx. class文件。
3.自定义注解?语法格式?
[修饰符列表]@interface 注解类型名{
}
4.注解使用语法格式是:
@注解类型名
注解可以出现在类上、属性上、方法上、变量上、注解上
5.JDK自带哪些注解?
标识性注解
@Override 这个注解只能注解方法,这个注解是给编译器参考的,和运行阶段没有关系。
凡是Java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器报错。
表示方法声明旨在覆盖超类型中的方法声明。
API帮助文档介绍:
如果使用此注释类型注释方法,则除非至少满足以下条件之一,否则需要编译器生成错误消息:
该方法将覆盖或实现在超类型中声明的方法。 该方法具有与Object中声明的任何公共方法的覆盖相同的签名 。
Deprecated 用@Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择(已过时)。
~元注解有哪些?
元注解:用来标注“注解类型”的注解,称为元注解。
常见的元注解
Target 表示"被标注的注解位置只出现在哪个位置上"
Retention 表示"被标注的注解"最终保存在哪里。
@Retention(RetentionPolicy.SOURCCE):表示该注解只能被保留在Java源文件中。
@Retention(RetentionPolicy.RUNTIME):表示该注解可以被反射
@Retention(RetentionPolicy.CLASS):表示该注解被保存在class 文件中,并且可以保留在字节码文件中
~
注解的属性是value 有且只有一个时,可以省略value =;
其他情况不行
注解中的属性可以是哪种类型?
byte short int long double boolean char String Class 枚举类型
(以上的数组形式)
数组中只有一个元素,大括号可以省略。
~通过反射机制读取注解?
~了解注解的实现原理以及应用?
转载:https://blog.csdn.net/LJCC_c/article/details/109539254