Java基础

JDK1.8新特性

1.default关键使得接口中可以有default修饰的普通方法,并且只要实现了这个接口就可以直接调用这个方法,大大简化代码量。遗留问题?抽象类也可以有普通方法,那么有了这个新特性后抽象类的地位何在?
2.Lambda表达式,简化代码开发,这里紧接着引入函数式接口,只包含了一个抽象方法的接口
3.stream流,如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。

1.int和Integer

1字节=8位 1B=8bit
为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型。
基本类型:
byte 1字节
short 2字节
int 4字节
long 8字节
float 4字节
double 8字节
char 2字节
boolean 1字节
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

Integer的缓存机制:
整型字面量的值在-128到127之间(常用),那么不会new新的Integer 对象,而是直接引用常量池中的Integer对象

2.String

String底层是一个final修饰的字符数组,故每次操作String都会new一个String对象,和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

3.==和equals

对于基本类型,==直接比较值。对于引用类型,都是比较地址。不过String中,重写了equals(当然hashcode也要跟着重写),比较是内容而不是地址了。

4.hashcode和equals

Object类中关于hashCode()方法的注释写道:
如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同这是规定,别问为什么人家源码里的注释就这么规定的,其实我也想知道为什么这么规定
如果两个对象的hashCode相同,它们并不一定相同。
所以在hashmap插入新的key时,先判断两个key的hashcode是否相同,相同的话(产生哈希冲突)再看equals返回的是不是true,好处就是减少equals的次数。Object类中的hashcode是C/C++写的本地方法,返回的是对象的地址的哈希值,equals不重写的话也是比较对象的内存地址值。但是在hashmap中两个内容相同的key对象是可以有不同的地址的,所以就要重写hashcode和equals了。

举个例子:
两个学生对象s1,s2,他们姓名、学号、年龄都相同,插入hashmap中,我们当然要当做这是同一个key,equals方法因为重写过了所以返回的是true,如果不跟着重写hashcode,s1和s2的hashcode肯定不相等,那么就不能保证key的唯一性,s1和s2都会存在于hashmap中。

异常和错误

在这里插入图片描述
Exception和Error有共同父类Throwable。
Exception(异常)带来的问题一般不大,应用程序自己可以处理。
Error(错误)表示运行应用程序中较严重问题。一般来说都在JVM层面,与代码编写没有关系,比如OOM。

Exception又分为两大类,Runtime Exception(运行时异常)和非Runtime Exception,Runtime Exception顾名思义就是运行时候发生的异常,继承这个类的异常常见的有空指针异常、数组越界异常,这种异常我们一般是不去捕获处理的(要做的是去修改代码,肯定是代码写错了,一般不处理的话,它就自动向上一级级抛异常,结果就是主程序终止,多线程环境下就是对应的线程终止)。
除了RuntimeException及其子类以外(unchecked exceptions 不可查异常),其他的Exception类及其子类都属于可查异常(checked exceptions)也可以说是非运行时异常,不处理的话,编译都没法通过,例如如IOException、SQLException等,一般我们try catch处理或者throws Exception,把异常抛出去。

finally关键字
执行在return之后,return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally语句后才真正开始返回。

关键字

final修饰一个类时,表明这个类不能被继承。对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

重载(Overload)和重写(Override)

重载要求在一个类中,方法名一样,参数不一样,返回类型也可以不一样。
重写是发生在子类,是对父类方法的重新定义,要求方法名,参数,返回类型都要一样。

Java接口

由于Java不支持多继承,但是java中的接口支持多继承,即一个子接口可以有多个父接口。(接口的作用是用来扩展对象的功能,一个子接口继承多个父接口,说明子接口扩展了多个功能,当类实现接口时,类就扩展了相应的功能)。与继承相比,接口有更高的灵活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性。

抽象类和接口

先说相同点:
他们都不能直接实例化对象拿来用,接口等着被实现,抽象类等着被继承。这里注意,抽象类的子类如果想要自己能够实例化就必须把父类的所有抽象方法都给具体实现了,少一个都不行,少一个的话那就还是抽象类,依然不能自己实例化对象。

1抽象类方法可以含有普通方法 抽象类是对类的抽象,可以有main方法,也可以有构造器,也可以有非抽象方法(这个子类可以不重写)。
2接口中不能有普通方法,接口是对行为的抽象,它除了没有方法体的方法外,啥也没有而且接口必须public去修饰,公开的,等着别人来实现他呢,他自己啥也干不了。

面向对象

继承:子类拥有父类的内容,子类还可以有自己特有的内容
封装:将细节隐藏起来,方法就是一种封装
多态:提高程序扩展性,便于修改代码
抽象:把事物的共性找出来归为一个类

final, finally, finalize

final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。所以String也不能被继承
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法

反射创建对象

  • 方法1:通过类对象调用newInstance()方法,例如:String.class.newInstance()
  • 方法2:通过类对象的getConstructor()或getDeclaredConstructor()方法获得构造器(Constructor)对象并调用其newInstance()方法创建对象,例如:String.class.getConstructor(String.class).newInstance(“Hello”);

反射机制和优缺点

反射可以在程序运行的期间动态的构造一个类的对象调用它的方法。因为你可能并不清楚你将来会用到哪个类的实例对象,等到你想用的时候就用反射的方法,而不用直接写死代码,增加了程序的灵活性(优点)。
原理:JVM会为每个类创建一个java.lang.Class类的实例,通过该对象可以获取这个类的信息,然后通过使用java.lang.reflect包下的API以达到各种动态需求。缺点也来了:使用反射性能较低需要解析字节码,将内存中的对象进行解析。
另外反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
获取类的Class对象:
1.实例对象的getClass方法,这种方法比较捞,既然我都有实例对象了,我还反射干啥==

Student stu1 = new Student();
Class stuClass = stu1.getClass();

2.类名.class

Class stuClass2 = Student.class

3.Class类的forName()方法的返回值,输入全限定类名

Class stuClass3 = Class.forName("fanshe.Student");

创建实例

1.使用Class对象的newInstance()方法

Object stu = stuClass3.newInstance();

2.先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象

Constructor constructor = stuClass3.getConstructor(Student.class);
Object obj = constructor.newInstance();

反射图解

在这里插入图片描述

内存泄漏和内存溢出

内存泄漏:该回收的对象没有被回收,如果长生命周期的对象持有短生命周期的引用,就很可能会出现内存泄露。比如说,一个生命周期长的对象,例如一个静态变量(和进程共生死)引用了一个普通对象,然后这个普通对象没用了(给对象赋予了空值null,不用它了),但是由于静态变量不能被GC,所以导致这个生命周期短的普通对象也不能被GC,就占着堆的空间了,一次两次内存泄漏倒也不会怎么样,次数一多就会发生内存溢出OOM。

内存溢出:就是你写的程序要申请内存空间的时候,发现空间不够了,就会报OOM。

内存溢出怎么处理?

1.-Xms -Xmx设大一点

2.检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

3.检查代码里面有没有死锁和没出口的递归

泛型

就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
好处:
1.类型安全,敲代码的时候如果先设定了泛型你后面写的类型和前面设定好的泛型不一致就会提醒你编译出错。
2.减少程序员手动的强制类型转换,避免出错。

Object类的方法

clone() 创建并返回此对象的一个副本(深拷贝浅拷贝)。equals(Object obj) 指示某个其他对象是否与此对象“相等”。finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。getClass()返回一个对象的运行时类。hashCode()返回该对象的哈希码值。 notify()唤醒在此对象监视器上等待的单个线程。 notifyAll()唤醒在此对象监视器上等待的所有线程。toString()返回该对象的字符串表示。wait()导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。

Java集合

在这里插入图片描述
LinkedHashMap可以认为是HashMap+LinkedList,也就是说,它使用HashMap操作数据结构,也用LinkedList维护插入元素的先后顺序.
重写equals和hashcode可以保证set集合元素的唯一性。

Java Web

http get和post
两者都是http请求方式,get一般用于查询,post用于更新。
get请求提交的数据会在地址栏显示出来,不安全,而且受限于地址栏的长度限制导致传输数据受限;
post请求提交的数据在请求体中,更安全,传输数据的大小也不会受限。

servlet
1本质是java编写的服务端程序,生产动态Web内容。
2生命周期:加载Servlet的class---->实例化Servlet----->调用Servlet的init方法完成初始化---->响应请求(Servlet的service方法)----->Servlet容器关闭时(Servlet的destory方法)

Servlet API中forward() 与redirect()的区别
在这里插入图片描述
浏览器发送请求给服务器1,服务器1搞不定转发这个请求给服务器2去做,然后服务器2响应这个请求。其中,地址栏内容没发生变化,请求就一次。
在这里插入图片描述
在这里插入图片描述
session和cookie的区别
都是会话跟踪技术。
cookie数据存放在浏览器上,不安全,保存的数据小不超过4K
session放在服务端,占服务器资源
将登陆信息等重要信息存放为SESSION
其他信息如果需要保留,可以放在COOKIE中,比如购物车

反射

可以动态的加载类,提高了程序的灵活性。
应用场景:
idea的代码提示功能
Spring

Spring

spring中的bean是线程安全的,因为里面没放数据。
控制层(Controller)->业务层(Service)->数据层(dao)
IOC
控制反转,原本Sevice层调Dao直接new对象就好了,控制权在Sevice,现在Spring解析XML文件后,在工厂类中通过反射机制得到Bean对象,控制权交给了Spring,如果Sevice需要再给Sevice注入这个依赖。达到解耦的目的。
AOP
面向切面编程
思想就是把核心和非核心业务逻辑分离,也可以说是横向扩展一些功能,达到解耦的目的。
原理:基于动态代理(JDK和CGLIB两种动态代理)设计模式,不改变原来代码的情况下,增强一些功能。
例如:日志功能,权限验证,事务,效率检查。
代理:真实对象执行核心业务,而
代理对象
负责其他相关业务的处理。
静态代理,一个真实对象就要配一个代理对象,代码太多了
动态代理,代理对象通过反射机制动态生成,代码量减少。

SpringBoot

自动配置原理:
SpringBoot启动的时候加载主配置类,开启了自动配置功能
也就是这个注解@EnableAutoConfiguration:
内部是去扫描jar包类路径下METAINF/spring.factorie,筛选出以EnableAutoConfiguration为key的数据,加载在IOC容器中,实现自动配置。

HashMap

底层是哈希表,数组(桶)加链表。
散列算法
index=HashCode(key)&(lengh-1)
问题:为什么数组长度必须是2的幂次
当数组长度为2的次幂时,这个&操作就等价于%操作,位运算效率更高。
16-1=15 对应二进制数1111与hachcode(key)后四位相与,得到的index随机性更高。
另外hashmap中key的hashcode获取方法是内部自定义的,是让key原本的hashcode右移16位,然后高16位和低16位相异或,获取新的hashcode值,这样操作后新的hachcode值随机性更高。
哈希冲突:数组长度有限,散列函数设计的再好,也会有不同的key占同一个操槽位slot的时候。

PUT
流程:
懒加载机制,第一put时才会生成哈希表。插入的键值对,先给他包装成一个Node对象,(key value hash next),根据散列算法找到对的index值,如果该slot==null,直接插入即可,如果有,则先判断key,相同的话就replace掉,如果不同就从链表头开始迭代,一个个判断下去,没有的话就插入在链表的末尾(1.7是从头插进去的,多线程扩容会产生死链)。最后还要判断链表的长度有没有大于8数组长度有没有大于64,如果都大于了就要开始树化。扩容除了要达到阈值,还需要此时刚好发生了哈希碰撞。

死链产生:
1.7头插法扩容在并发的时候会导致死链
A线程执行到这一步在这里插入图片描述
的时候,此时e=3next=7
B线程也执行了扩容,等到B线程执行完,如下图:
在这里插入图片描述

这个时候A线程又醒了,继续按照他睡着前的e=3,next=7执行,就会得到

在这里插入图片描述
右图的7上面还有一个3,形成3->7->3这样的死链。
因为头插法,扩容之后的链表顺序是颠倒的,A线程醒来后,按照常规顺序头插,就会形成3->7->3这样的死链。

由于B线程执行,7的next指向的是3(7.netx本应该指向null,这样while循环就退出了),也就是3又会加进来,形成3->7->3这样的死链,并把这个结果更新到内存。然后C线程来访问的时候如果恰好访问到这个索引位置,就CPU溢满了。

JDK1.8的扩容优化

没有rehash这步了。
扩容时机:元素大于等于数组容量x负载因子,且此时发生了哈希碰撞。
slot本身是null不用处理
slot上只有一个Node,扩容后放到新数组对应的位置。
slot上已经链化,把链表拆成高位链和低位链两段,怎么得来的高位链和低位链?(让Node的hash值和数组长度(假设是16,二进制数就是10000)&运算之后,得到的结果要么全为0(低位链),要么有一个为1(高位链),),低位链放在运来的位置即可,高位链放在原来的index+数组的长度16后对应的位置。
slot上已经树化,其实红黑树的TreeNode节点依然保留着next字段,内部依然还维护着一个链表,然后根据这个链表向前面一样操作就是了。如果拆分完后发现长度小于6就从树退化回链表。

红黑树问题

树化时机:数组长度大于64且链表长度达到8
为什么转成红黑树不转成AVL树?
红黑树是一种接近平衡的二叉树,但是不是严格平衡,红黑树最多能保证左右子树深度差距不超过一倍,AVL树只能不超过1。这样既可以保证树的高度不会太高,又可以减少树的变动。

红黑树性质
节点不是黑色就是红色
两个红色节点不能连在一起
根节点是黑色的,叶子节点也都是黑色的

变色和左右旋
记不下来==
左旋:当前节点的左子树变成其父节点的右子树,然后他父节点变成了他的左子树。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

HashMap两个参数

HashMap未扩容前默认长度是16Capacity,扩容后X2.
LoadFactor加载因子0.75,设置大了那么哈希碰撞就会变多,链表长度也就上去了,查询效率低。如果设置小了,那么就会动不动就扩容,导致空间的利用率降低。Jdk1.8后,当链表长度大于8(为什么是8?根据泊松分布,由于哈希碰撞在每个桶里增加链表的长度的概率指数型下降,到8个的时候概率趋近于零)之后且数组长度大于64,链表转为红黑树,目的是提高查找效率,小于6后退回链表(扩容后重写映射可能会使红黑树退化为两个链表)。

HashMap非线程安全,单线程下效率高。
Hashtable锁住整个Map,虽然线程安全但是效率很低,就像串行一样,阻塞的很厉害。
ConcurrentHashMap (1.7)采用分段锁(把整个Map划分成16个Segment(赛格门特)再加锁,写数据的时候用synchronize加锁,读数据的时候Volatile保证可见性),既可以保证线程安全,并且效率高。1.8之后加入了很多CAS。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值