赞
踩
牛客面经:
2.getClass方法
final方法,获得运行时类型。
3.toString方法
该方法用得比较多,一般子类都有覆盖。
4.finalize方法
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
5.equals方法
该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。
6.hashCode方法
该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
7.wait方法
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
8.notify方法
该方法唤醒在该对象上等待的某个线程。
9.notifyAll方法
该方法唤醒在该对象上等待的所有线程。
读未提交(read uncommitted)
允许读取未提交的数据。
读已提交(read committed)
只允许读取已提交数据,但不要求可重复读。比如,在事务两次读取一个数据项期
间,另一个事务更新了该数据项并提交。
可重复读(repeatable read)
只允许读取已提交数据,而且在一个事务两次读取一个数据项期间,其他事务不得更新该数据。但该事务不要求与其他事务可串行化。比如,在两次统计查询中,另一个事务可以插入一些记录,当这些记录中有符合查询条件的,那么就会产生幻读。
可串行化(serializable)
看起来事务就好像是串行执行的一样。一个事务结束后,另一个事务才开始执行。
隔离性依次增高。
read uncommitted read committed repeatable read serializable
联合索引:联合索引是指对表的多个列进行索引
最左前缀法则。
覆盖索引:InnoDB 存储引擎支持覆盖索引 (covering index), 即从辅助索引中就可以得到要查询的信息,而不需要回表。
方式一:继承Thread类的方式
创建一个继承于Thread类的子类
重写Thread类中的run():将此线程要执行的操作声明在run()
创建Thread的子类的对象
调用此对象的start():①启动线程 ②调用当前线程的run()方法
方式二:实现Runnable接口的方式
创建一个实现Runnable接口的类
实现Runnable接口中的抽象方法:run():将创建的线程要执行的操作声明在此方法中
创建Runnable接口实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
调用Thread类中的start():① 启动线程 ② 调用线程的run() —>调用Runnable接口实现类的run()
以下两种方式是jdk1.5新增的!
方式三:实现Callable接口
与使用Runnable相比, Callable功能更强大些
实现的call()方法相比run()方法,可以返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果
Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
FutureTask是Futrue接口的唯一的实现类
FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
方式四:使用线程池
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
Map接口概述:
将键映射到值的对象。(我们可以根据键快速地查找到值)
Map 中键是唯一的 (不能包含重复的键)
每个键最多只能映射到一个值。
如果所有的键都是小整数,我们可以用一个数组来实现的符号表,将键作为数组的索引,而数组中对应的位置存储键关联的值。
哈希表的核心算法可以分为两步。
用哈希函数将键转换为数组中的一个索引。理想情况下不同的键都能转换成不同的索引值。当然这只是理想情况下,所以我们需要处理两个或者多个键都散列到相同索引值的情况 (哈希碰撞)。
处理碰撞冲突。
a. 开放地址法
线性探测法, 平方探测法, 再散列法…
b. 拉链法
HashMap概述:
基于哈希表的Map接口实现。
允许null键和null值。
不保证映射的顺序,特别是它不保证该顺序恒久不变。
不同步。
HashMap VS Hashtable
相同点:
底层的数据结构都是哈希表
不同点:
a. HashMap是不同步的, Hashtable是同步的
b. HashMap可以允许null键和null值,Hashtable不允许null键和null值
LinkedHashMap概述:
HashMap的子类
Map 接口的哈希表和链表实现,具有可预知的迭代顺序.
链表定义了迭代顺序,该迭代顺序就是键值对的插入顺序。
不同步。
TreeMap概述:
底层的数据结构是红黑树。
如果创建对象时,没有传入 Comparator 对象,键将按自然顺序进行排序。
如果创建对象时,传入了 Comparator 对象,键将按 Comparator 进行排序。
不同步。
Java 给我们提供了哪些并发安全的 Map?我们应该选择哪一个?
Hashtable (看一下hashtable源码会发现hashtable有很多方法用的synchronized nchronized 方法,这样的话锁对象就是this,我们知道哈希表table的每个index下挂着一个链表,假如我在index=1的链表下进行增删查操作是不会影响index=2下的数据的,但由于hashtable的锁对象是this,所以,它不允许我们进行以上操作,尽管以上操作并不会发生线程安全,所以,hashtable的并发度是很低的 )
Collections.synchronizedMap(Map map) (Collections是一个工具类,并发度和hashtable一样)
static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
返回指定有序映射支持的同步(线程安全的)有序映射。
ConcurrentHashMap(用它)
Hashtable 锁对象是 this对象
Collections.synchronizedMap(Map map) 锁对象是 mutex
JDK8 ConcurrentHashMap 锁对象是链表的头结点。 大大提高并发度。这样的话,关于起那么的index=1和index=2上两条链表的操作就可以同时进行了。
阻塞自己
wait() (在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前)导致当前线程等待/阻塞
阻塞功能:
在哪个线程中调用了 对象a.wait(),该方法导致调用wait方法的线程处于阻塞状态
唤醒条件:
在 其他线程 调用 此对象的 notify() 方法或 notifyAll()
即因为哪个对象的wait()而处于阻塞状态,要唤醒该线程,必须在同一个对象上调用其notify() 或者 notifyAll()
a.wait() 其他线程 a.notify() 或者notifyAll方法
前提条件:
当前线程 必须 拥有此对象监视器 (当前线程必须持有这个锁对象)
即,当前线程如果要想正确运行wait(),前提条件是 必须在当前线程所持有的 锁对象.wait() 。
说白了,你想用锁对象.wait(),首先得写在synchronized (o) { }中。
wait() 方法除了使线程阻塞之外它还做了什么:
该线程 发布对此监视器(监视器即锁对象)的所有权 (当前线程一旦调用了wait()方法,当前线程释放锁) 并 等待
即阻塞当前线程
直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。
然后该线程将等到重新获得对监视器的所有权后才能继续执行。就是说唤醒你之后你得取争抢锁对象,才能继续往下执行
通知别人
notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的
notifyAll()
唤醒在此对象监视器上等待的所有线程
BLOCKED
一个线程因为等待临界区的锁被阻塞产生的状态
Lock 或者synchronize 关键字产生的状态
进入 blocked 状态是被动的。
WAITING
一个线程进入了锁,但是需要等待其他线程执行某些操作。时间不确定
当wait,join,park方法调用时,进入waiting状态。前提是这个线程已经拥有锁了。
进入 waiting 状态是线程主动的。
public static void main(String[] args) throws Exception {
String className = "org.b3log.solo.util.Test";
Class clasz = Class.forName(className);
Test t = (Test) clasz.newInstance();
}
public static void main(String[] args) throws Exception {
Constructor<Test> constructor;
try {
constructor = Test.class.getConstructor();
Test t = constructor.newInstance();
} catch (InstantiationException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException |
NoSuchMethodException |
SecurityException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Test t1 = new Test("张三");
Test t2 = (Test) t1.clone();
System.out.println(t2.getName());
}
public class Test implements Serializable{ private String name; public Test() { } public Test(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public static void main(String[] args) throws Exception { String filePath = "sample.txt"; Test t1 = new Test("张三"); try { FileOutputStream fileOutputStream = new FileOutputStream(filePath); ObjectOutputStream outputStream = new ObjectOutputStream(fileOutputStream); outputStream.writeObject(t1); outputStream.flush(); outputStream.close(); FileInputStream fileInputStream = new FileInputStream(filePath); ObjectInputStream inputStream = new ObjectInputStream(fileInputStream); Test t2 = (Test) inputStream.readObject(); inputStream.close(); System.out.println(t2.getName()); } catch (Exception ee) { ee.printStackTrace(); } } }
懒加载
立即加载
public class Singleton {
private Singleton();
static Singleton st = new Singleton();
public static Singleton getInstance(){
return st;
}
}
package com.cskaoyan; import com.cskaoyan.service.HelloService; import com.cskaoyan.service.HelloServiceImpl; import org.junit.Test; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 动态代理 */ public class ProxyTest { /** * 没有使用动态代理 */ @Test public void mytest() { //获得委托类对象 HelloService helloService = new HelloServiceImpl(); System.out.println("起床"); helloService.sayHello(); System.out.println("编程"); } /** * 使用jdk动态代理 */ @Test public void mytest2() { //在hello world之前 起床 //hello world之后 编程 //先获得委托类对象的实例 HelloService helloService = new HelloServiceImpl(); //去获得一个代理对象来完成增强 → jdk动态代理获得增强对象(代理对象) //classloader都是和委托类对象相关的 //interfaces都是和委托类对象相关的 HelloService helloServiceProxy = (HelloService) Proxy.newProxyInstance(HelloServiceImpl.class.getClassLoader(), helloService.getClass().getInterfaces(), new InvocationHandler() { //invoke中 //1.是要去执行委托类的方法 //2.可以去做增强 //返回值:Object 对应委托类方法执行的返回值 //参数: // proxy :代理对象 // method: 委托类方法的method // args: 委托类方法的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("起床"); //invoke方法中可以使用反射来执行委托类的方法 //第一个参数是委托类对象,而不是代理对象 Object invoke = method.invoke(helloService, args); System.out.println("编程"); return invoke; } }); //helloServiceProxy.sayHello(); //使用代理对象去执行才会增强 //helloService.sayHello(); //使用委托对象只会输出hello world helloServiceProxy.method2(); } }
IOC用于实例化类对象,管理类对象
IOC:Inverse of Controll 控制反转
控制:实例的生成权
反转:由应用程序反转给spring
容器:容器是放置实例对象的地方 → Spring容器、IOC容器
原先实例我们想用的时候自己new出来(主动的过程);到了Spring阶段,把实例的生成权交给了Spring容器,由Spring容器进行生成并管理,当我们想用的时候就从容器中取出来。
DI:Dependency Injection 依赖注入
依赖:
谁依赖谁? 应用程序依赖Spring容器
为什么依赖? Spring容器包含了应用程序必须的内容
注入:
谁注入谁? Spring容器注入给应用程序
注入了什么?应用程序运行所必须的资源
AOP:面向切面编程
作用:给容器中的组件做增强
之前做增强是通过oop(面向对象)编程,继承父类(静态代理)或传入委托类对象(动态代理)。
之前是使用动态代理给一个类生成代理对象
aop是给容器中的组件批量生成代理对象 → 把要增强的方法放到一起 → 这个范围称为切面。aop是一个范围更广的增强
public class 快速排序 { public static void main(String[] args) { int[] nums = new int[]{1, 9, 5, 4, 2, 7, 0, 2, 6, 8}; QuickSort(nums, 0, nums.length - 1); System.out.println(Arrays.toString(nums)); } public static void QuickSort(int nums[], int low, int high) { int temp; int i = low, j = high; if (low < high) { temp = nums[low]; while (i < j) { while (i < j && nums[j] >= temp) j--; if (i < j) { nums[i++] = nums[j]; } while (i < j && nums[i] < temp) i++; if (i < j) { nums[j--] = nums[i]; } } nums[i] = temp; QuickSort(nums, low, i - 1); QuickSort(nums, i + 1, high); } } }
public ListNode mergeTwoLists(ListNode h1, ListNode h2) { ListNode head = new ListNode(-1); ListNode h = head; while(h1 != null && h2 != null){ if(h1.val < h2.val){ head.next = h1; h1 = h1.next; }else{ head.next = h2; h2 = h2.next; } head = head.next; } if(h1 != null) head.next = h1; if(h2 != null) head.next = h2; return h.next; }
package com.leetcode; import java.util.Arrays; /** * @author liushihao <liushihao@kuaishou.com> * Created on 2021/2/25 10:32 下午 */ public class MergeSort { public static void main(String[] args) { int[] nums = new int[]{1, 9, 5, 4, 2, 7, 0, 2, 6, 8}; mergeSort(nums, 0, nums.length - 1); System.out.println(Arrays.toString(nums)); } //二路归并排序 public static void mergeSort(int nums[], int low, int high) { if (low < high) { int mid = (low + high) / 2; mergeSort(nums, low, mid); mergeSort(nums, mid + 1, high); merge(nums, low, mid, high); } } public static void merge(int[] a, int low, int mid, int high) { int[] temp = new int[high - low + 1]; int i = low; int j = mid + 1; int k = 0; // 把较小的数先移到新数组中 while (i <= mid && j <= high) { if (a[i] < a[j]) { temp[k++] = a[i++]; } else { temp[k++] = a[j++]; } } // 把左边剩余的数移入数组 while (i <= mid) { temp[k++] = a[i++]; } // 把右边边剩余的数移入数组 while (j <= high) { temp[k++] = a[j++]; } // 把新数组中的数覆盖nums数组 for (int x = 0; x < temp.length; x++) { a[x + low] = temp[x]; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。