public class Run {
public static void main(String[] args) {
int i = 1;
int j = 1;
System.out.println(i == j); // true
String a = "b";
String b = "b";
System.out.println(a == b); // true
String c = new String("b") ;
System.out.println(a == c); // false
}
}
-
== 对于基本类型
int
long
double
float
boolean
short
char
来说对比较的是值是否相等 -
== 对于引用类型 比较的是引用是否相同
-
直接创建的字符串他们的引用地址相同 , 而 new 创建的字符串是开辟新的内存空间
String 的创建方式 直接等于字符串 或者 new String(字符串)
-
public class Run {
public static void main(String[] args) {
Eq eq1 = new Eq(1);
Eq eq2 = new Eq(1);
System.out.println(eq1.equals(eq2)); //false
}
private static class Eq{
public int anInt;
public Eq(int anInt) {
this.anInt = anInt;
}
}
}
String a = "b";
String c = new String("b") ;
System.out.println(a.equals(c));//true
-
equals是
java.lang.Object
的一个方法public boolean equals(Object obj) { return (this == obj); }
==
根据上述 引用类型对比的是引用是否相同 (地址是否相同),关键字new
都是开辟新的内存地址所以它不相同。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
-
java.lang.String
重写了equals 方法 , 将String
分解成char
数组 进行比较是否相等 -
java.lang.Integer
java.lang.Double
基础类型的包装类也同样重写了equals
方法
- 不是
public class Run {
public static void main(String[] args) {
String s1 = "123";
String s2 = new String("123");
System.out.println(String.format("s1: %d \t s2: %d", s1.hashCode(), s2.hashCode()));
System.out.println(s1.equals(s2));
}
}
- 通过
new String()
来进行验证
-
final 修饰类 ,该类不能被继承
-
final 修饰方法,该方法不能重写
-
final 修饰变量,我们将这个变量称为常量 , 常量必须初始化
public static final double PI = 3.14159265358979323846;
- 编译
- 加载
- 链接
- 验证
- 准备
- 解析
- 运行
创建用
new
, 对象实体与对象引用的区别:内存地址,修改对象引用会修改引用对象本身,深浅拷贝。
- 方法区(Method Area)
- 已经加载的类
- 常量
- 静态变量
- 堆(Heap)
- 存放对象实例
- 栈
- 虚拟机栈(VM Stack)
- 本地方法栈(Native Method Area)
- 程序计数区
- 行号指示器: 在分支结构、循环结构、跳转、异常处理、线程恢复时需要使用
- 引用计数: java虚拟机内部有一个引用计数器,当一个类被使用计数器+1,当一个类不再使用(或引用失效)计数器-1 , GC 回收计数器为0的类(对象)
- 根搜索算法: 假设A继承B继承C, 从A开始向上寻找B,继续由B向上寻找C, 其中某一环没有引用进行GC
- 标记清除: 2个步骤,标记、清除,标记每一个需要进行垃圾回收的对象,标记完成后一并清理。
- 特点: 空间碎片过多,当需要连续分配一个较大内存容量时不容易分配。
- 标记整理: 2个步骤,标记、整理,标记每一个需要进行垃圾回收的对象,标记完成后将所有对象向一端进行移动。
- 特点: 和标记清除相比空间碎片不会产生,新的对象永远在已有对象之后。
- 复制算法:
- 作用域不同: 局部变量只在函数内,语句内;成员变量在整个类中都有效
- 默认值: 局部变量没有默认值;成员变量有默认值
- 字符型:char
- 字符串:String
不可以,构造方法唯一,一个类只有一个构造方法
- 重载:Overload
- 表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同)。
- 重写:Override
- 参数列表,返回类型必须完全相同,修改了执行逻辑
创建用
new
, 对象实体与对象引用的区别:内存地址
- 构造方法的作用:对象的创建,类的初始化
- 没有构造方法可以正常运行,默认有一个空参构造,实现了有参构造后默认空参构造消失
- String 属于不可变对象,即每次操作都会产生新的String 对象
StringBuffer
和StringBuilder
在原有基础上进行操作
-
StringBuffer
线程安全 ,StringBuilder
是线程不安全的-
java.lang.StringBuilder#append(char)
@Override public StringBuilder append(char c) { super.append(c); return this; }
-
java.lang.StringBuffer#append(char)
@Override public synchronized StringBuffer append(char c) { toStringCache = null; super.append(c); return this; }
-
-
StringBuffer
的方法有synchronized
修饰
-
不需要
-
下述代码可以直接运行
public abstract class Absc { void hello() { System.out.println("hello"); } }
- 抽象类不能直接实例化,普通类可以直接实例化 (
new
) - 抽象类可以具有抽象方法 , 普通类不能具有抽象方法
-
不能****
-
抽象类是给其他类继承,重写抽象方法来拓展。
final
修饰后 这个类将不能被继承
接口 | 抽象类 | |
---|---|---|
实现方式 | implements |
extends |
构造函数 | 没有构造函数 | 有构造函数 |
实现数量 | 可以实现多个接口 | 只能继承一个抽象类 |
访问修饰符 | 默认使用public |
任意访问修饰符 |
- 功能
- 输入流
- 输出流
- 类型
- 子节流
- 字符流
java.util.Collection
是一个接口- 定义了集合对象的一些通用操作
java.util.Collections
是一个类- 这是一个包装类,提供很多静态方法,不能被实例化,
元素是否有序 | 元素是否重复 | ||
---|---|---|---|
List | 是 | 是 | |
Set | AbstractSet | 否 | 否 |
HashSet | 否 | 否 | |
TreeSet | 是 | 否 | |
Map | AbstractMap | 否 | Key 唯一 Value 允许重复 |
HashMap | 否 | Key 唯一 Value 允许重复 | |
TreeMap | 是 | Key 唯一 Value 允许重复 |
-
put
方法-
java.util.HashMap#put
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
-
java.util.Hashtable#put
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; }
-
-
HashMap允许key 和 value 为Null
-
HashTable不允许key 和 value 为Null
- HashMap 线程不安全
- HashTable 线程安全
-
ArrayList
transient Object[] elementData;
数组 -
LinkedList
-
transient Node<E> first; transient Node<E> last; private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }
双向链表
-
-
ArrayList 索引访问
-
public E get(int index) { rangeCheck(index); return elementData(index); } E elementData(int index) { return (E) elementData[index]; }
-
-
LinkedList遍历访问
-
public E get(int index) { checkElementIndex(index); return node(index).item; } Node<E> node(int index) { // assert isElementIndex(index); if (index < (size >> 1)) { Node<E> x = first; for (int i = 0; i < index; i++) x = x.next; return x; } else { Node<E> x = last; for (int i = size - 1; i > index; i--) x = x.prev; return x; } }
-
-
ArrayList
-
遍历直到是删除元素进行删除
public E remove(int index) { rangeCheck(index); modCount++; E oldValue = elementData(index); int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
-
-
LinkedList
-
修改节点即可
public E remove(int index) { checkElementIndex(index); return unlink(node(index)); } E unlink(Node<E> x) { // assert x != null; final E element = x.item; final Node<E> next = x.next; final Node<E> prev = x.prev; if (prev == null) { first = next; } else { prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
-
-
ArrayList 线程不安全
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
-
Vector线程安全
synchronized
关键字public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
-
ArrayList扩大50%
int newCapacity = oldCapacity + (oldCapacity >> 1);
-
Vector扩大100%
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
/**
* Retrieves and removes the head of this queue. This method differs
* from {@link #poll poll} only in that it throws an exception if this
* queue is empty.
*
* @return the head of this queue
* @throws NoSuchElementException if this queue is empty
*/
E remove();
/**
* Retrieves and removes the head of this queue,
* or returns {@code null} if this queue is empty.
*
* @return the head of this queue, or {@code null} if this queue is empty
*/
E poll();
- 从queue中删除第一个元素,返回第一个元素
-
异常抛出
remove()
如果元素没有抛出NoSuchElementException
poll()
则返回null
java.util.Collections#unmodifiableCollection
public class UnchangeableGather {
public static void main(String[] args) {
List<Integer> integers = new ArrayList<Integer>();
integers.add(1);
Collection<Integer> integers1 = Collections.unmodifiableCollection(integers);
integers1.add(1);
System.out.println(integers.size());
}
}
- 并行
- 多个处理器同时处理多个任务
- 并发
- 多个任务在同一个cpu核上按照时间片轮流执行
- 继承
Thread
重写run
- 实现
Runnable
接口 - 实现
Callable
接口
java.lang.Thread.State
public enum State {
// 未启动
NEW,
// 执行中
RUNNABLE,
// 阻塞中
BLOCKED,
// 持久等待
WAITING,
// 等待到指定时间重写唤醒
TIMED_WAITING,
// 执行完成
TERMINATED;
}
- 类
java.lang.Thread#sleep(long)
java.lang.Object#wait()
- 锁的释放
- sleep不释放
- wait释放
- 调用次数
run()
没有限制start()
只能调用一次
- 对应操作项
run()
对应线程运行时的代码start()
对应线程
notify()
只会唤醒一个线程notifyAll()
唤醒所有线程
位于 java.util.concurrent.Executors
下
- newFixedThreadPool
- newWorkStealingPool
- newSingleThreadExecutor
- newCachedThreadPool
- newSingleThreadScheduledExecutor
- newScheduledThreadPool
java.util.concurrent.ThreadPoolExecutor
// 能接受新提交的任务,并且也能处理阻塞队列中的任务;
private static final int RUNNING = -1 << COUNT_BITS;
// 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。(finalize() 方法在执行过程中也会调用shutdown()方法进入该状态)
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于 RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态;
private static final int STOP = 1 << COUNT_BITS;
//如果所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。
private static final int TIDYING = 2 << COUNT_BITS;
// 在terminated() 方法执行完后进入该状态,默认terminated()方法中什么也没有做。
private static final int TERMINATED = 3 << COUNT_BITS;
进入TERMINATED的条件如下:
- 线程池不是RUNNING状态;
- 线程池状态不是TIDYING状态或TERMINATED状态;
- 如果线程池状态是SHUTDOWN并且workerQueue为空;
- workerCount为0;
- 设置TIDYING状态成功。
方法 | runnable 任务 | callable任务 |
---|---|---|
execute() | 执行 | 不执行 |
submit() | 执行 | 执行 |
- 使用synchronized
- 使用Lock
- 使用安全类
- volate
- 变量 修饰符
- 实现变量的修改可见性,不能保证原子性
- 不会阻塞线程
- synchronized
- 类 、方法 、 代码段 修饰符
- 保证变量的修改可见性, 保证原子性
- 会阻塞线程
-
synchronized
- 给类 、方法、代码段 添加锁
- 不需要释放锁,这个行为JVM自动实现
- 不能确定是否获取锁
-
lock
- 给代码段添加锁
- 需要手动获取锁, 释放锁否则会陷入死锁的问题(
unlock
) - 可以确定是否成功获取锁
-
模拟一个spring项目加载配置文件
public class SpringLoad { public static void main(String[] args) { SpringLoad springLoad = new SpringLoad(); springLoad.load(); } private void load() { long startTime = System.currentTimeMillis(); loadProject(); long endTime = System.currentTimeMillis(); System.out.printf("加载总共耗时:%d 毫秒\n", endTime - startTime); } private void loadProject() { loadSpringMvc(); loadMyBatis(); loadSpring(); } private void loadSpringMvc() { loadXML("spring-mvc.xml", 1); } private void loadMyBatis() { loadXML("mybatis.xml", 2); } private void loadSpring() { loadXML("spring.xml", 3); } /*** * 加载文件 * @param xml * @param loadSec */ private void loadXML(String xml, int loadSec) { try { long startTime = System.currentTimeMillis(); long milliseconds = TimeUnit.SECONDS.toMillis(loadSec); Thread.sleep(milliseconds); long endTime = System.currentTimeMillis(); System.out.printf("[线程 : %s] 加载%s 耗时: %d 毫秒\n", Thread.currentThread().getName(), xml, endTime - startTime ); } catch (Exception e) { e.printStackTrace(); } } }
[线程 : main] 加载spring-mvc.xml 耗时: 1001 毫秒
[线程 : main] 加载mybatis.xml 耗时: 2000 毫秒
[线程 : main] 加载spring.xml 耗时: 3000 毫秒
加载总共耗时:6017 毫秒
可以发现串行消费时间 = 各个方法的总和
public class SpringLoad2 {
public static void main(String[] args) {
SpringLoad2 springLoad2 = new SpringLoad2();
springLoad2.loadProject();
}
public void loadProject() {
try {
long start = System.currentTimeMillis();
ExecutorService service = Executors.newFixedThreadPool(3);
CompletionService completionService = new ExecutorCompletionService(service);
completionService.submit(this::loadSpringMvc, null);
completionService.submit(this::loadMyBatis, null);
completionService.submit(this::loadSpring, null);
int count = 0;
while (count < 3) {
if (completionService.poll() != null) {
count++;
}
}
service.shutdown();
System.out.println(System.currentTimeMillis() - start);
} catch (Exception e) {
e.printStackTrace();
}
}
private void loadSpringMvc() {
loadXML("spring-mvc.xml", 1);
}
private void loadMyBatis() {
loadXML("mybatis.xml", 2);
}
private void loadSpring() {
loadXML("spring.xml", 3);
}
/***
* 加载文件
* @param xml
* @param loadSec
*/
private void loadXML(String xml, int loadSec) {
try {
long startTime = System.currentTimeMillis();
long milliseconds = TimeUnit.SECONDS.toMillis(loadSec);
Thread.sleep(milliseconds);
long endTime = System.currentTimeMillis();
System.out.printf("[线程 : %s] 加载%s 耗时: %d 毫秒\n",
Thread.currentThread().getName(),
xml,
endTime - startTime
);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 最长的消耗时间为运行总时长
程序运行时可以指定该类的方法、属性,并且可以调用
public class Run {
public static void main(String[] args) throws Exception {
Cc cccc = new Cc();
Class<? extends Cc> aClass = cccc.getClass();
Method setAname = aClass.getDeclaredMethod("setAname", String.class);
Method sayHi = aClass.getDeclaredMethod("sayHi", null);
sayHi.setAccessible(true);
setAname.invoke(cccc, "张三");
sayHi.invoke(cccc, null);
}
private static class Cc {
private String aname;
private void sayHi() {
System.out.println("hello" + aname);
}
public String getAname() {
return aname;
}
public void setAname(String aname) {
this.aname = aname;
}
}
}
- throw 真实抛出一个异常
- throws 声明式抛出一个异常 (可能发生)
catch
和 finally
两个只能省略其中一个 ,try
必须保留
-
执行 , 在catch中的return 结束后执行
-
Redis 与 memcached 相比有哪些优势?
memcached 只能存储字符串, Redis 存储类型多样 Redis 速度快 Redis 可以数据持久化
- Redis 支持哪几种数据类型?
- string
- list
- hash
- zset
- set
- Redis 有哪几种数据淘汰策略?
- 内存到达限制,谁也不删除,返回错误
- 回收最少使用的key
- 回收最少使用的key,且key必须在过期集合中
- 随机回收
- 随即回收,且在过期集合中
- 回收过期集合中存活时间短的
- 为什么 Redis 需要把所有数据放到内存中?
读写速度考虑.
-
Redis 一个字符串类型的值能存储最大容量是多少?
-
512 mb
-
Redis 主要消耗什么物理资源?
内存资源
- Redis 集群方案应该怎么做?都有哪些方案?
- 主从复制
- 哨兵模式
- Redis-Cluster
- 说说 Redis 哈希槽的概念?
存在16384 个哈希槽,每一个key经过CRC15校验对13684取模,判断落入哪一个槽内
- Redis 有哪些适合的场景?
- session
- 队列
- 排行榜
- 发布者/订阅者模式
- Redis 支持的 Java 客户端都有哪些?官方推荐用哪个?
- Jedis
- Redisson 官方推荐Redisson