字符串池最初是空的,由类String单独维护。
当调用intern方法时,如果池中已经包含一个与equals(object)方法确定的字符串对象相等的字符串,则返回池中的字符串。否则,此字符串对象将添加到池中,并返回对此字符串对象的引用。
因此,对于任何两个字符串s和t,s.intern()==t.intern()是真的当且仅当s.equals(t)为真时。
return: 与此字符串具有相同内容的字符串,但保证来自唯一字符串池。
参考java官方文档:
public String intern()
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.
Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
1.final:
(1)修饰类:无法被继承
(2)修饰方法:
父类的final方法是public不可被子类重写,可被继承
父类的final方法是private,由于private不被继承,子类可以定义一个相同的方法
(3)修饰属性:
成员变量:表示常量,一般和static联合使用,被final修饰值不可变
基本数据类型:初始化后值不可变
引用数据类型:初始化后不能指向其他的对象
2.static:
(1)修饰方法:调用时用类名.方法名
(2)修饰变量:不可修饰局部变量,在对象之间共享值或者方便访问变量时使用static来修饰
(3)静态代码块:在类加载时执行
Java的基本数据类型、拆装箱(深入版)
(1)Integer是int的包装类,int则是java的一种基本数据类型
(2)Integer变量必须实例化后才能使用,而int变量不需要
(3)Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 。
(4)Integer的默认值是null,int的默认值是0
import static静态导入是JDK1.5中的新特性。
一般我们导入一个类都用 import 包名.类名;
而静态导入是这样:import static 包名.类名.*;
这里的多了个static,还有就是类名后面多了个 .* 。意思是导入这个类里的静态成员(静态方法、静态变量)。当然,也可以只导入某个静态方法,只要把 .* 换成静态方法名就行了。然后在这个类中,就可以直接用方法名调用静态方法,而不必用“类名.方法名()” 的方式来调用。这种方法的好处就是可以简化一些操作,例如一些工具类的静态方法,如果使了静态导入,就可以像使用自己的方法一样使用这些静态方法。
不过在使用静态导入之前,我们必须了解下面几点:
1.静态导入可能会让代码更加难以阅读
2.import static和static import不能替换位置
3.如果同时导入的两个类中又有重命名的静态成员,会出现编译器错误。例如Integer类和Long类的MAX_VALUE。
4.可以导入的静态成员包括静态对象引用、静态常量和静态方法。
我这里就不总结了:StringBuffer和StringBuilder的区别
如果有对象参与相加,编译器没法处理,结果为新的对象
字符串常量直接相加,会先经过编译器处理,调用StringBuffer或StringBuilder的append方法;
ThreadLocal
Future
必须重写hashCode,参与equals函数的字段,也必须都参与hashCode 的计算。
java学习笔记之优先队列实现原理
2.键值对存储元素
了解rehash方法吗
rehash在计算索引位置index时,HashTable进行了一个与运算过程(hash & 0x7FFFFFFF),为什么需要做一步操作,这么做有什么好处?
for (int i = oldCapacity ; i-- > 0 ;) {
for (IdentityHashtableEntry old = oldTable[i] ; old != null ; ) {
IdentityHashtableEntry e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = newTable[index];
newTable[index] = e;
}
}
hashtable进行扩容的时候,会进行rehash,通过与运算,旧数组中的元素的hashCode要么和新数组中的hashCode相同,要么在为旧的hashCode+2^n,也就是能尽可能保持原有链表的顺序。
二叉树,平衡二叉树,红黑树,B-树、B+树、B*树的区别
十大经典排序算法最强总结(含JAVA代码实现)
希尔排序、快速排序、归并排序、堆排序
它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
说一说建堆的过程
1.首先根据序列构建一个完全二叉树
2.在完全二叉树的基础上,从最后一个非叶结点开始调整:比较三个元素的大小–自己,它的左孩子,右孩子。分为三种情况:
自己最大,不用调整
左孩子最大,交换该非叶结点与其左孩子的值,并考察以左孩子为根的子树是否满足大顶堆的要求,不满足递归向下处理
右孩子最大,交换该非叶结点与其右孩子的值,并考察以右孩子为根的子树是否满足大顶堆的要求,不满足递归向下处理
jvm内存模型jdk1.7和jdk1.8的区别
java类的加载机制
父类静态属性(成员变量) > 父类静态代码块 > 子类静态属性 > 子类静态代码块 >
父类非静态属性 > 父类非静态代码块 > 父类构造器 >
子类非静态属性 > 子类非静态代码块 > 子类构造器
讲一下垃圾回收器都有哪些?
JVM常见垃圾回收算法
从GC-Root开始进行可达性分析
新生代:复制算法
老年代:标记-压缩算法
动态代理
1、sleep是线程中的方法,但是wait是Object中的方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后退出阻塞),但是wait需要(不指定时间需要被别人中断)。
实现多线程的四种方式
讲解视频
1、拦截器是基于Java的反射机制的,而过滤器是基于函数回调
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次
执行顺序:过滤前 - 拦截前 - Action处理 - 拦截后 -过滤后。个人认为过滤是一个横向的过程,首先把客户端提交的内容进行过滤(例如未登录用户不能访问内部页面的处理);过滤通过后,拦截器将检查用户提交数据的验证,做一些前期的数据处理,接着把处理后的数据发给对应的Action;Action处理完成返回后,拦截器还可以做其他过程,再向上返回到过滤器的后续操作。
过滤器(Filter):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。
拦截器(Interceptor):在一个流程正在进行的时候,你希望干预它的进展,甚至终止它进行,这是拦截器做的事情。
监听器(Listener):当一个事件发生的时候,你希望获得这个事件发生的详细信息,而并不想干预这个事件本身的进程,这就要用到监听器。
java读取properties配置文件的几种方式
1.基于InputStream读取配置文件
2.通过Spring中的PropertiesLoaderUtils工具类进行获取
3.通过 java.util.ResourceBundle 类读取
1.读:打开文件 – 产生句柄 – 权限 – 转换成流 – 读取 — 关闭句柄 – 关闭文件du
2.写:打开zhi文件 – 产生句柄 – 权限 – 加锁 — 是否追加写入 – 写流 — 关闭句柄 – 关闭文件
进程间通讯的7种方式
关于常见的几种缓存算法
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信
号等),某进程内的线程在其他进程不可见;
线程和进程关系示意图
1.应用层为 文件传输,电子邮件,文件服务,虚zhi拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet
2.传输层 提供端对端的接dao口 TCP,UDP
3.网络层 为数据包选择路由 IP,ICMP,RIP,OSPF,BGP,IGMP
4.数据链路层, 传输有地址的帧以及错误检测功能 SLIP,CSLIP,PPP,ARP,RARP,MTU
5.物理层 以二进制数据形式在物理媒体上传输数据
TCP/IP 是互联网相关各类协议族的总称。
TCP/IP 的分层管理:
TCP/IP协议里最重要的一点就是分层。TCP/IP协议族按层次分别为 应用层,传输层,网络层,数据链路层,物理层。当然也有按不同的模型分为4层或者7层的。
tcp和udp的区别
同步IO和异步IO的区别
ForkJoin
1.内连接只显示两表中有关联的数据
2.左连接显示左表所有数据,右表没有对应的数据用NULL补齐,多了的数据删除
3.右连接显示右表所有数据,左表没有对应的数据用NULL对齐,多了的数据删除
Mysql数据库存储引擎及索引的数据结构
深入理解Spring的两大特征(IOC和AOP)
1.AOP的底层是动态代理
追问:有几种实现方式
有两种:
(1)JDK动态代理:要求对象必须实现接口
为什么?Java动态代理为什么要求必须实现顶级接口
(2)CGLIB动态代理
注意什么:只要可继承的类都可以实现,所以被final修饰的不能实现该动态代理
2.类型有五种
说说执行顺序:可以通过order来指定执行顺序 ,数字越小优先级越高
给定一个数组和一个数字n,找出数组中两个数字和为n的组合,一组即可
1.辅助空间,hash表
public static int[] method1(int[] arr, int number){
int len = arr.length;
if (len == 0 ||len == 1) return new int[]{};
Map<Integer, Integer> tempMap = new HashMap<>();
for (int i = 0; i < len; i++){
tempMap.put(arr[i] , i);
}
for (int i = 0; i < len; i++){
if (tempMap.containsKey(number - arr[i]) && tempMap.get(number - arr[i]) != i){
return new int[]{arr[i] , number - arr[i]};
}
}
return new int[]{};
}
2.给数组排序
public static int[] method2(int[] arr, int number){
int len = arr.length;
if (len == 0 ||len == 1) return new int[]{};
Arrays.sort(arr);
for (int i = 0, j = len - 1; i < j;){
if (arr[i] + arr[j] > number){
j--;
}else if (arr[i] + arr[j] < number){
i++;
}else {
return new int[]{arr[i] , arr[j]};
}
}
return new int[]{};
}
给定一个单链表,在链表中把第 L 个节点到第 R 个节点这一部分进行反转。
输入描述:
null
输出描述:
null
备注
示例1:
输入 [1,2,3,4,5],1,3 输出 {3,2,1,4,5}
class ListNode{
int value;
ListNode next;
public ListNode() {
}
public ListNode(int value) {
this.value = value;
}
}
public class List {
public static ListNode InverseheadByIndex(ListNode head,int left,int right){
ListNode temp = head;
ListNode preLeft = null;//左边下标的前一个
ListNode afterRight = null;//右边下标的后一个
int len;
for (len = 1; temp != null; len++){
if (len + 1 == left) preLeft = temp;
if (len - 1 == right) afterRight = temp;
temp = temp.next;
}
if (left > right || right > len || left < 1) return null;
temp = preLeft == null ? head : preLeft.next;
ListNode tempNext = temp.next;
temp.next = afterRight;
ListNode node = null;
while (tempNext != afterRight){
node = tempNext.next;
tempNext.next = temp;
temp = tempNext;
tempNext = node;
}
if (preLeft != null){
preLeft.next = temp;
return head;
}
return temp;
}
public static void main(String[] args) {
ListNode node = new ListNode(1);
ListNode node1 = new ListNode(2);
node.next = node1;
ListNode node2 = new ListNode(3);
node1.next = node2;
ListNode node3 = new ListNode(4);
node2.next = node3;
ListNode node4 = new ListNode( 5);
node3.next = node4;
ListNode result = InverseheadByIndex(node,2,3);
while (result != null){
System.out.println(result.value);
result = result.next;
}
}
}
反转部分单向链表——思路