从概念上讲JDK是JAVA开发工具,用它来开发JAVA程序,里面有很多基础类库和jre。
JRE是JAVA运行环境,开发出来的JAVA程序只要有JRE(jvm)就能运行。
JVM是JAVA虚拟机,是JRE的一部分,对于JAVA类的操作基本上都是在JVM上完成的
4种整数类型
byte 1字节 8位 范围 -28到28
short 2字节 16位 范围 -216到216
int 4字节 32位 范围 约21亿
long 8字节 64位 末尾加L
byte第一位是符号位 总共8位 int short long都是这样第一位是符号位
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zq5Myo9Y-1670078357516)(…/img/image-20220803144125412.png)]
2种浮点类型
float 4字节
double 8字节
磁盘中保存形式:
int类型组成部分:符号位和数值位
float类型组成部分:符号位、阶位、数值位
double类型组成部分:符号位、阶位、数值位
double的数值位高达52位,碾压int类型
平均精度:float
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5d5QBiLI-1670078357517)(…/img/image-20220802114129503.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeY7PVIX-1670078357518)(…/img/image-20220913221812575.png)]
有效位数表示精确度 再长会精度丢失
有效位数:二进制从第一个不为0的数开始数 有多少位
1种字符类型
“char”
选择的编码决定 unicode编码 一般2字节 16位
UTF-8 大多数2字节16位
UTF-16 2或者4字节 在Java中,char类型描述了UTF-16编码中的一个代码单元
UTF-32 4字节 固定32位
charAt 获取第几个字符
1种布尔类型
“boolean” 4字节
**理论上消耗1bit就可以存储一个布尔类型 因为只有true和 false*** ***计算机最小的物理存储单元 --8bit--1B****
在java布尔类型 底层是int类型实现的 所以在java中占32bit 牺牲空间提升读取速度的做法
在Java中Boolean类型底层用 int 类型表示,所以分配给它的是32个字节—>在CPU高速缓存区里存储单位是缓存行,每一个缓存行存储64字节,因为通过总线读取时需要通过电压传递,缓存行里面的数据的读取是需要排队的,数据越小排队的越多,所以让boolean类型数据变大,运行就会越快
int类型和float类型两者都是4字节,都是32bit 排列组合的数量是一样的 ,int表示的是等长边距 ,表示范围小,而float类型的范围要大, 所以精度低
单精度浮点数虽然由32位组成,但是其中有1位符号位,8位指数,只有23位是数值尾数。也就是说它的精度仅为23位。
而整型int也由32位组成,它只有1位是符号位,剩下的31位都是数值尾数。
int类型比float类型平均精度高(在-1~1的时候float的精度更高),在float类型范围比int类型大
用bigdecimal和biginteger大数据类型解决
要知道 int类型float类型 都是32比特
int类型是1bit符号位 +31比特数值位
float类型是1bit符号位 + 8bit指数位+23bit
而且每个数组只能存储同一种类型的数据
float x=10/4;—2.0,后面是int类型
java对数据的变量有着严格的类型要求,它们在内存中都有着固定的大小。比如int------32bit float------32bit ,每个类型在内存中都代表着一种类型 类型转换的时候非常严格
i++的通俗的解释即是先赋值再自增,其实这里赋值的值是从操作数栈取的值,也就是说先将i的值压入栈中,
而自增是局部变量表的值自增。
而++i则相反,是先自增后赋值,就是局部变量表的自增,然后把局部变量表的值压入栈中。
1、<<左移
a=a<<3; 二进制左移三位 相当于乘2^3
N进制左移M位 相当于乘N^M
2、>>右移动 相当于除法 和<<相反
需要注意的是 向右移动的时候需要看符号位了 因为补的是符号位
如果右移3三 符号位是1 那么补上三个1
是0补上三个0
3、>>>右移 和上面的区别就是补的不是符号位了 规定补0
5、每一个数都是转换成二进制 二进制都可以用相加的形式表示出来
所以每一个数都可以用位运算
基本类型的 == 比较的是值是否相等
引用类型的 == 比较的是地址是否相等
equals()、hashCode()、getClass()、notify()、notifyAll()、wait()、wait(long *,int *)、wait(long *,int *)、toString()
Object 中equals 比较两个对象的物理地址是否相等, 比较两个句柄指向的地址(是否是相同的引用),指向的是否是同一个对象
字符串的equals重写后比较的是字面值是否相等
重写equals也必须重写hashCode方法,为什么?因为hashmap需要equals和hashCode配合使用(先hashcode进行比较找到大概的位置,如果相等了就使用equals进行精确判断,如果不相等就不用再次判断了,减少使用equals开销)
然而重写了equals,s1.equals(s2)返回true,根据hashcode的规则,两个对象相等其哈希值一定相等,所以矛盾就产生了,因此重写equals一定要重写hashcode.
equals和hashCode如何组合使用?
toString 输出类的路径以及对象在内存中的地址
getClass 反射获取类信息
notify唤醒进入等待线程的过程
notifyAll唤醒全部进入等待线程的过程
wait等待线程
无限整数类型和 无限小数类型(BigInteger和BigDecimal)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p3GoS18a-1670078357518)(…/img/image-20220805145651488.png)]
1、十进制转换成几进制 就一直对这个数求余
3、N进制转十进制
7进制转换成十进制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HM1ZL7p-1670078357519)(…/img/image-20220805145205341.png)]
特点:
1、数组是连续的 紧挨在一起的
只有基本类型的一维数组在 物理地址上才是连续的
引用类型 二维数组在物理地址不连续
2、数组的数据存在堆内存中
2、下标从0开始
3、只能存储相同类型的数据
4、不能原地扩展
计算机只记录第一个比特点的位置
数组只记录第一点的地址代表整个的地址 所以数组是连续的
因为物理地址是连续的 因为数组存储的都是相同的数据类型 内存大小是相同的 我们通过截取的方式 拿int类型来说一次截取32位
为什么从0开始?
拿int类型来说 int类型有32位
通过截取的方式 一次截取32比特
从0开始 0*32=0 然后从0截取32位
然后到1的时候 就从第32位继续截取下一个数
如果从1开始的话 就是1*32=32 然后从32开始截取32位 最前面的32位就丢失了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-viEORrgT-1670078357519)(…/img/image-20220901124842738.png)]
数组为什么不能原地扩展?
因为数组是连续的 如果原地扩展的话 一个数移动所有的数都会跟着移动
消耗的时间太多
1、String 存储的核心是char数组实现的,数组在物理上是连续的。String是final修饰的char[]不可变,是不可以在(char[])内存原地址修改数据。
2、StringBuffer:StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。
StringBuffer底层是一个char数组,就是相当于用char数组一次性申请足够大的空间,能进行拼接 和缓冲区一样 一次性申请足够大的空间
初始化容量是16
多线程操作字符串缓冲区下操作大量数据 StringBuffer;
3、StringBuilder类也代表可变字符串对象。StringBuilder和StringBuffer基本相似,单线程操作字符串缓冲区下操作大量数据 StringBuilder**
字符串的拼接使用的是append()方法
StringBuilder、StringBuffer(锁)的区别?
StringBuffer几乎所有的方法都使用synchronized实现了同步,线程比较安全,在多线程系统中可以保证数据同步,但是效率比较低;而StringBuilder 没有实现同步,线程不安全,在多线程系统中不能使用 StringBuilder,但是效率比较高。
4、如何优化StringBuffer性能?
一开始一个合适的初始化容量
尽量减少底层数组的扩容次数
5、总结1:String字符串具有不可变性,当字符串重新赋值时,不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值。
总结2:当字符串进行拼接时,也不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值
深拷贝:1、基本类型的拷贝都是深拷贝,因为基本类型占据的空间只在栈中,int a=1,b=a
2、引用类型的深拷贝,就是会在堆中创建一个新的对象,然后复制这个对象的内容,这时栈中会有两个句柄分别指向这两个对象
比如序列化,我要把一个对象从一台电脑传到另一条电脑,就需要用到深拷贝,复制整个对象,然后传过去
浅拷贝, 就是复制一个对象的内存地址,指向的还是堆中的同一个对象
传递的是前面句柄的地址 是引用传递
传递的是后面的值的地址 是值传递
java中只有按值传递
引用传递
如果使用句柄访问的话,Java堆中将可能会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYHPXBhY-1670078357520)(…/img/image-20220804231757544.png)]
值传递
如果使用直接指针访问的话,Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关 信息,reference中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问的开销。。。使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多也是一项极为可观的执行成本
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F64sS6UQ-1670078357520)(…/img/image-20220804231817672.png)]
Final
final修饰的类(无法被继承)
final修饰的方法(无法被覆盖,重写),但是可以被重载
final修饰的局部变量(只能赋一次值)
final修饰的引用一旦指向某个对象,则不能再指向其他对象,但是该引用指向的对象的内部数据是可以修改的
final防止指令重排序,保障多线程下的数据安全;
不能同时用abstract和final修饰类(abstract修饰的类是抽象类,抽象类是用于被子类继承的,和final起相反的作用);Final修饰的方法不能被重写,但是子类可以用父类中final修饰的方法
Finally:
finally 不管try catch执不执行,它一定会执行
进行IO操作一定要记住关闭连接
io操作:(内存从磁盘,内存从网络传输大量数据,都是以毫秒为单位的,磁盘转一圈5毫秒,内存到网络传输一般是一毫秒到几十毫秒)
作用:和网络相关,和IO相关的都需要建立连接的,这种连接是会占据存储内存空间的,所以需要关闭连接,finally块一般是用来关闭(释放)物理资源(数据库连接,网络连接,磁盘文件等
当try中有return时执行顺序:return语句并不是函数的最终出口,如果有finally语句,这在return之后还会执行finally(return的值会暂存在栈里面,等待finally执行后再返回)
总结:
try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:
情况一:如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况:
如果return的数据是基本数据类型,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
1、修饰的变量属于类变量,被所有该类产生的对象共享
2、修饰的方法中不能使用非静态方法,但是非静态方法可以调用静态方法
3、修饰静态块 作用:给类进行初始化。随着类的加载而执行,且只执行一次,在main方法之前 ,最开始执行
4、Static 修饰的是静态的 一般可以通过调用类直接访问
但是可以被随便修改 所以常和final一起使用
父类静态块–子类静态块–父类块–父类构造方法–子类块–子类构造方法
静态初始化块:
静态块和静态变量谁在前面就谁先初始化
作用:给类进行初始化。随着类的加载而执行,且只执行一次,在main方法之前 ,最开始执行
封装(对类信息进行封装) 只对外提供简单的访问入口
setter和getter作为操作入口
或者通过构造器访问,实例化对象的时候赋值
面试问题:访问器是如何保证线程安全的?
访问器(set get方法)本身不能保证数据安全 **需要自己写方法(深拷贝)**才能避免被修改 保证安全性 。set get方法只在多线程的情况下进行保护
继承:
一个类可以被多个类继承
继承可以继承父类公共的属性、方法**、除了构造方法、public声明的都可以继承**
提高代码的复用性
多态:
父类的句柄指向子类的对象
向上转型:
父类类型 变量名 = new 子类类型
接口名称 变量名 = new 实现类名称
向下转型:
子类类型 变量名 = (子类类型) 父类对象
实现类名称 对象名称 = (实现类名称) 接口名称
转型前使用instanceof判断
父类对象构造方法中“this()”和“super()”不能同时出现,也就是“this()”和“super()”都只能出现在构造方法的第一行
调用父类被子类重写的方法(super.);当在子类对象中,子类想访问父类的东西,可以使用“super.”的方式访问
调用父类的构造方法;**super()**默认存在构造方法的第一行
This关键字 当前对象
1、this是一个引用,保存内存地址指向自身,就是储存当前对象地址,代表当前对象,所以静态方法不能有this
2、this可以在实例方法和构造方法中出现,那个对象调用这个实例方法,哪个对象就是this
3.this大部分可以省略,不能省略的时候
this.属性名 :一个方法中局部变量和成员变量的名称相同时,就需要用到 this.成员变量 = 局部变量
this.方法名 :让类中的一个方法去调用另一个方法或者实例变量,
this() 只能在构造方法中使用,表示当前构造方法调用本类其他的构造方法,目的是代码复用,但是只能出现在构造方法的第一行,不能和super()同时使用,因为它俩规定都必须在第一行,super是在无参构造器中默认存在一个super()
重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
重写发生在子类与父类之间,重写涉及到多态,子类对父类的方法重新定义,子类覆盖父类的方法,重写要求子类被重写方法与父类被重写方法有相同的参数列表,有兼容的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常
相同点:
**1、**都是抽象的,不能被实例化
2、当一个非抽象类实现接口或者抽象类的时候,必须将接口和抽象类中所有的抽象方法全部实现
不同点:
1.接口中的方法默认都是抽象的,java8开始有默认方法(default);
抽象类中可以有非抽象方法;
2.接口中的变量都是final类型的
抽象类中不一定
3.一个类可以实现多个接口,但是只能继承一个抽象类
4.一个类要实现接口的所有方法,不一定要实现抽象类中的所有方法
5.抽象类是对象的抽象,是一种模板设计‘;接口是对行为的抽象,是一种行为设计
接口方法的冲突
1、 一个类实现两个接口,两个接口的方法都一样可以吗?可以!
但是如果两个方法的名称一样,但是两个方法的参数不一样可以吗? 可以,就是方法的重载,再重载一个带参数方法就行了
但是如果两个方法的名称一样,但是两个方法的返回值类型不一样可以吗? 不可以!
2、如果先在一个接口中将一个方法定义为默认方法, 然后又在超类中定义了同样的方法, 会发生什么情况?
超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
为什么有包装类?
因为泛型不支持基本类型 只能是引用类型
**自动装箱和自动拆箱?**基本类型转成包装类叫装箱 ,包装类转成基本类型叫拆箱
面试题128陷阱? Integer在 -128~127之间比较才是true 所以需要比较的时候需要拆箱 intValue()方法拆箱
如果我们的数值在-128-127之间的数值都存储在有一个cache数组当中,该数组相当于一个缓存,当我们在-128-127之间进行自动装箱的时候,我们就直接返回该值在内存当中的地址,所以在-128-127之间的数值用==进行比较是相等的。而不在这个区间的数,需要新开辟一个内存空间,所以不相等。
正常类有的功能枚举都有
枚举是限定了对象个数的类,无法new对象
一个枚举就是一个对象,会调用对象的构造方法
构造方法是私有(private)类型,防止通过new的方式创建新的对象,因为每次调用对象都会调用构造方法
名而且有相同参数类型的默认方法会被忽略。
为什么有包装类?
因为泛型不支持基本类型 只能是引用类型
**自动装箱和自动拆箱?**基本类型转成包装类叫装箱 ,包装类转成基本类型叫拆箱
面试题128陷阱? Integer在 -128~127之间比较才是true 所以需要比较的时候需要拆箱 intValue()方法拆箱
如果我们的数值在-128-127之间的数值都存储在有一个cache数组当中,该数组相当于一个缓存,当我们在-128-127之间进行自动装箱的时候,我们就直接返回该值在内存当中的地址,所以在-128-127之间的数值用==进行比较是相等的。而不在这个区间的数,需要新开辟一个内存空间,所以不相等。
正常类有的功能枚举都有
枚举是限定了对象个数的类,无法new对象
一个枚举就是一个对象,会调用对象的构造方法
构造方法是私有(private)类型,防止通过new的方式创建新的对象,因为每次调用对象都会调用构造方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZvkBHK6u-1670078357521)(…/img/image-20220819221258722.png)]