目录
一.图书馆管理系统的总结
1.与父类的联系
父类建立一个数组,向下转型的时候子类使用构造方法,要把需要使用父类的成员变量放在构造方法里,如果放在外面就不可以
2.重中之重
这一步的向下转型
构建dowork方法
构建work把一个个串联起来
通过建立接口把所有的操作放在一起
在通过引入书架这个变量,再联系起书
二.每日一题 重排链表
解法1
我的思路就是构建三个节点,让虚拟头节点的位置放在头结点上因为根据题意.我分析到,最后一个节点移到虚拟头结点前面一个位置,然后虚拟头结点往前移两个节点,最后一个节点再次移到虚拟节点前面一个位置,如此循环.直到虚拟头结点移到尾节点(奇数情况下)或者尾结点下一个(偶数情况下)结束.或者还有一种情况也会结束,就是只剩下一个节点就不需要交换,这里我对他的判断就是前驱节点没有移动.
class Solution {
public void reorderList(ListNode head) {
ListNode cr=head;//构建虚拟头结点
while(cr!=null&&cr.next!=null){
ListNode prev=cr;
ListNode cur=cr.next;//每次循环做准备
while(cur.next!=null){
cur=cur.next;
prev=prev.next;
}//让cur移到尾结点
if(prev==cr){
return;
}//如果没有进入循环就说明没有动就说明可以结束了
cur.next=cr.next;
cr.next=cur;//让尾结点移到虚拟头结点前面
cr=cr.next.next;//让虚拟头结点往前移动
prev.next=null;//手动置空最后一个节点的next
}
}
解法2
class Solution {
public void reorderList(ListNode head) {
ListNode cr=head;//构建虚拟头结点
while(cr!=null&&cr.next!=null){
ListNode prev=cr;
ListNode cur=cr.next;//每次循环做准备
while(cur.next!=null){
cur=cur.next;
prev=prev.next;
}//让cur移到尾结点
if(prev==cr){
return;
}//如果没有进入循环就说明没有动就说明可以结束了
cur.next=cr.next;
cr.next=cur;//让尾结点移到虚拟头结点前面
cr=cr.next.next;//让虚拟头结点往前移动
prev.next=null;//手动置空最后一个节点的next
}
}
解法三 存储
三 Clonable 接口和深拷贝
native修饰的方法是无法看见的,是底层C语言实现的
查看clone的底层源码.可以看到他的返回值是一个Object类.这里我们就需要强制转型
我们发现还是不行,因为如果一个类想要被实现克隆.就需要一个克隆接口
我们却发现这个接口是一个空接口
空接口也是标志接口表示他是被克隆的
我们必须要重写克隆方法
一个对象要被克隆,要在他所在的类实现cloneable接口,并且重写clone方法,还要抛出异常和接收异常
在这个例子里我们是深拷贝
但是如果我们在被克隆里的一个类再添加一个类的成员
我们会发现被克隆的仅仅是那个类的地址,并没有真正做到深拷贝
我们对一个对象进行改变,但是发现第二个对象也同样改变了,所以没有做到拷贝,
第二个对象里的对象还是指向了原来的位置->这里就是浅拷贝
我们需要里面的对象也被拷贝
这里我们需要定义一个tmp在克隆方法里来接收克隆的值,
再让这个新克隆的成员money再克隆一个m
具体实现
这里既然是M的克隆,那么我们就要在money这个类里建立CLONEable接口
并重写clone方法
因为返回值是object,我们需要强转.并返回tmp
这里我们发现
我们在拷贝后,改变person的m的值.并没有改变person1的m的值
所以我们可以得出结论
如果想要做到深拷贝,那就要对呗拷贝里所有的引用成员对应的类实现拷贝接口
一个对象要被克隆,要在他所在的类实现cloneable接口,并且重写clone方法,还要抛出异常和接收异常
四.抽象类和接口的区别
核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
五. String底层原理
java中,没有所谓的字符串以\0结尾
1.常见的构造string方式
// 方式一
String str = "Hello Bit";
// 方式二
String str2 = new String("Hello Bit");
// 方式三
char[] array = {'a', 'b', 'c'};
String str3 = new String(array)
注意事项:
"hello" 这样的字符串字面值常量, 类型也是 String.
String 也是引用类型. String str = "Hello"; 这样的代码内存布局如下
回忆 "引用"
我们曾经在讲数组的时候就提到了引用的概念.
引用类似于 C 语言中的指针, 只是在栈上开辟了一小块内存空间保存一个地址. 但是引用和指针又不太相同, 指
针能进行各种数字运算(指针+1)之类的, 但是引用不能, 这是一种 "没那么灵活" 的指针.
另外, 也可以把引用想象成一个标签, "贴" 到一个对象上. 一个对象可以贴一个标签, 也可以贴多个. 如果一个对
象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉.
Java 中数组, String, 以及自定义的类都是引用类型
2.String的底层原理
通过观察String类我们可以发现,他是被final修饰的,也就是无法被修改的.
这里我们可以来观察String下的成员
最主要的是下面这两个成员,一个是val还有一个是hash值
这里我们可以看到,value是一个字符型数组,也就是我们的数据会以数组的形式存储起来
3.String的传参
这里,str和chars都属于一个引用把他们对应在堆上的地址通过传参到func方法里
但是第一行代码的意思是建立一个字符串对象把地址赋值给s
第二行的代码是直接对数组进行操作
4.字符串常量池
5.字符串比较
一般来说字符串的比较我们有对应的方法.而这里的比较,.因为str1和str2都是引用变量
引用变量的名字就是地址,那么他们比较的就是地址.那么他们的地址会相同取决于他们在堆上的地址
之前我们也说到了String类里至少有两个成员一个是把字符串存储到数组的value一个是hash
这里我们就需要说一下字符串常量池了
2.池
那么什么是池呢
在java里有很多池
理解 "池" (pool)
"池" 是编程中的一种常见的, 重要的提升效率的方式, 我们会在未来的学习中遇到各种 "内存池", "线程池", "数据
库连接池" ....
然而池这样的概念不是计算机独有, 也是来自于生活中. 举个栗子:
现实生活中有一种女神, 称为 "绿茶", 在和高富帅谈着对象的同时, 还可能和别的屌丝搞暧昧. 这时候这个屌丝被
称为 "备胎". 那么为啥要有备胎? 因为一旦和高富帅分手了, 就可以立刻找备胎接盘, 这样 效率比较高.
如果这个女神, 同时在和很多个屌丝搞暧昧, 那么这些备胎就称为 备胎池.
3.字符串常量池
在JVM底层实际上会自动维护一个对象池(字符串常量池)
如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存
到这个对象池之中.
如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用
如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用
4.各种常量池
1.Class文件常量池:int a=10,这里面的 10就在字节码文件常量池里,他是在磁盘里
2.运行时常量池:把程序编译好的字节码文件,加载到JVM当中,会生成一个运行时常量池[方法区],实际上是Class文件常量池.
3.字符串常量池:主要存放字符串常量,本质上是一个哈希表"String Table",从1.8开始都放在 了堆里面
什么是字符串常量.就是双引号引起来的东西
什么是哈希表:数据结构->描述和组织数据的一种方式.
5.哈希表
数据结构->描述和组织数据的一种方式.
举例
哈希表存储数据的时候,会根据一个映射关系进行存储,如何映射需要设计一个函数(哈希函数).
上题的存储如果是用哈希表的话
假设此题的哈希函数是key%len
那么余数为2的数都会像节点一样放在表中为2元素下
哈希表的查找速度很快 时间复杂度O(1)
6.哈希表的实现
回到刚刚那道题
str1是创创建了一个字符串数组在堆上
str引用的就是他的地址
每个哈希表的节点
在哈希表上就存放着哈希节点的地址
这里解释一下,最下面的null表示相同映射的节点,此题没有所以就null了
第一个哈希值就是表示存放hello这个数组的对应的String对象的hash值
第二个就表示指向他的地址
所以哈希表最后的完整表达形式应该是这样的
String 存储的是val数组在堆上的地址,hash值如果有在hash表一样的就找到对应的节点.并把这个对象的地址
放到hash节点里,
而Str2指向了堆上的对象.
str2的val指向了hello数组 因为哈希表里的字符串常量池已经有了hello了所以不需要额外再建立直接指向就可以了
7.哈希表存储的变形形式
这里的结果就是true
六.字符串的拼接
我们在对应文件路径反推找到对应的out窗口打开powershell窗口
再构建一下再编译一下生成字节码文件
反汇编一下
这里我们可以看到编译的时候直接加在一起了
七字符串构建对象拼接
这里对于str1就不再赘述
重点就是str2
他建立了两个匿名对象
先看第一个
他先建立了一个数组"1",并连接到哈希表里
这个匿名对象并指向了这个数组
再次建立一个匿名对象,因为hash表已经有了,所以他直接指向.
两个拼接这里用到了StringBuilder类
他们俩拼接后指向了SB的val我们查看底层源码会发现,SB会调用toString方法
构建一个String把拼接的数据放到了String的val里
最后str2指向的就是最后这个对象
但是如果我们直接把str2的值通过intern方法放进哈希表里
这里的结果就是tuer
八.字符串比较函数equals()的本质
这里创建了很多对象
九.反射
通过反射可以看到私有的被隐藏的属性
String str = "Hello";
/// 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的.
Field valueField = String.class.getDeclaredField("value");
// 将这个字段的访问属性设为 true
valueField.setAccessible(true);
// 把 str 中的 value 属性获取到.
char[] value = (char[]) valueField.get(str);
// 修改 value 的值
value[0] = 'h';
System.out.println(str);
// 执行结果
hello
也就是第七行代码