本文是对一些java基础知识的整理,把以前印象笔记里面的所有慢慢搬到这个blog来html
为了方便就按照《Thinking In Java》的目录来编辑。java
这里面的内容均为面试题相关,可能的考点等android
这本书里面有些翻译不是很好,建议和英文版对照。程序员
抽象、封装、继承、多态web
面向对象设计方法主要特征:继承、封装、多态。面试
延伸点:反射会破坏代码的封装性(mock等)正则表达式
return | method | 解释 |
---|---|---|
protected Object | clone() | 建立并返回此对象的一个副本。 |
protected void | finalize() | 当垃圾回收器肯定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 |
void | notify() | 唤醒在此对象监视器上等待的单个线程。 |
void | notifyAll() | 唤醒在此对象监视器上等待的全部线程。 |
void | wait() | 在其余线程调用此对象的 notify()方法或 notifyAll()方法前,致使当前线程等待。 |
void | wait(long timeout) | 在其余线程调用此对象的 notify()方法或 notifyAll()方法,或者超过指定的时间量前,致使当前线程等待。 |
void | wait(long timeout, int nanos) | 在其余线程调用此对象的 notify()方法或 notifyAll()方法,或者其余某个线程中断当前线程,或者已超过某个实际时间量前,致使当前线程等待。 |
方法属于对象的成员,静态方法属于类的成员算法
Q:为何 wait, notify 和 notifyAll 这些方法不在 thread 类里面?编程
一个很明显的缘由是 JAVA 提供的锁是「对象级」 的而不是「线程级」的,每一个对象都有锁,经过线程得到。若是线程须要等待某些锁那么调用对象中的 wait()方法就有意义了。api
若是 wait()方法定义在 Thread 类中,线程正在等待的是哪一个锁就不明显了。
简单的说,因为 wait,notify 和 notifyAll 都是锁级别的操做,因此把他们定义在 Object 类中由于锁属于对象。
拓展:
在 Java 中,每一个对象都有两个池,锁(monitor)池和等待池
锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized 方法(或者 synchronized 块),因为这些线程在进入对象的 synchronized 方法以前必须先得到该对象的锁的拥有权,可是该对象的锁目前正被线程A拥有,因此这些线程就进入了该对象的锁池中。
等待池:假设一个线程A调用了某个对象的 wait()方法,线程A就会释放该对象的锁(由于wait()方法必须出如今 synchronized 中,这样天然在执行 wait()方法以前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。
若是另外的一个线程调用了相同对象的 notifyAll()方法,那么处于该对象的等待池中的线程就会所有进入该对象的锁池中,准备争夺锁的拥有权。若是另外的一个线程调用了相同对象的 notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池.
Q:为何 wait 和 notify 方法要在同步块中调用?
- 避免 IllegalMonitorStateException
- 避免任何在 wait 和 notify 之间潜在的竞态条件
Integer,Float,Double等都继承自Number类
名称 | 包装类型 | 初始化 | 其余 |
---|---|---|---|
boolean | Boolean | false | 1位 |
byte(1字节) | Byte | (byte)0 | ASCII码:空格:32;数字0到9:48到57,后面为大小写字母; byte 类型用在大型数组中节约空间,主要代替整数,由于 占用的空间只有 int 类型的四分之一 |
short(2字节) | Short | (short)0 | Short 数据类型也能够像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一; |
char(2字节) | Character | '\u0000'(null) | 单一的 16 位 Unicode 字符; |
int(4字节) | Integer | 0 | |
float(4字节) | Float | 0.0f | 精确到小数点后6位,在储存大型浮点数组的时候可节省内存空间; |
long(8字节) | Long | 0L | |
double(8字节) | Double | 0.0d | 精确到小数点后15位(默认的小数类型) |
BigInteger 任意精度的整数,不可变
BigDecimal 任何精度的定点数(商业计算),不可变,一般建议优先使用String构造方法
parseInt()是把String变成int的基础数据类型;
byte,short,char-> int -> long -> float -> double 不一样类型运算结果类型向右边靠齐。
浮点数到整数的转换是经过舍弃小数获得,而不是四舍五入
装箱过程是经过调用包装器的valueOf方法实现的,而拆箱过程是经过调用包装器的 xxxValue方法实现的。
大多数状况下二者相同,java.io.file,java.util.Date,java.lang.string,包装类(Integer,Double等)等实现了本身的equals,比较规则为:若是两个对象的类型一致,而且内容一致,则返回true。
valueOf()
方法的实现比较简单,就是先判断值是否在缓存池中,若是在的话就直接返回缓存池的内容。
在 Java 8 中,Integer
缓存池的大小默认为 -128~127,因此除非用 new Integer()
显式的建立对象,不然都是同一个对象(-128~127)
比较符号 | 比较对象 | 数据转型 |
---|---|---|
== | 同一个对象 | 有表达式会触发 |
equals | 同一个类型和内容 | 不触发 |
通常会考你“==”和equals的区别,数据转型,拆箱装箱
“==”和equals记住,前者相同,后者相等。前者会触发数据转型。
new Integer()新建对象
valueof装箱,xxxValue拆箱
方法 | 功能 | 返回值 |
---|---|---|
ceil() | 向上取整 | double |
floor() | 向下取整 | double |
round() | 原来的数字加上0.5后再向下取整 | int |
三种元素构成:数字+字符+$+下划线。
不能以数字开头+不能是关键字.
一个文件中能够有多个类,若是没有public类,则能够与任意类名相同,若是有public类则文件名必须与此类名相同,由于一个文件中只能有一个public类。若是文件中只有一个类,则文件名必须与类名相同
名称 | 功能 |
---|---|
Override | 标识当前的方法,是否覆盖了它的父类中的方法 |
Deprecated | 已通过期或者即将过时的方法,不推荐咱们使用 |
SuppressWarning | 屏蔽相应的警告 |
运算符 | 结合性 | 其余 |
---|---|---|
() [] . | 从左到右 | |
! +(正) -(负) ~ ++ -- | 从右向左 | ~n=-n-1 |
* / % | 从左向右 | %取余操做,只适用于整型 |
+(加) -(减) | 同上 | |
<< >> >>> | 同上 | 按位左(右)移运算符, 按位右移运补零运算符。 |
< <= > >= instanceof | 同上 | |
== != | 同上 | |
& | 同上 | 相对应位都是1,则结果为1,不然为0 |
^ | 同上 | 相对应位值相同,则结果为0,不然为1 |
或 | 同上 | 相对应位都是0,则结果为0,不然为1 |
&& | 同上 | 逻辑与(前面表达式为false,后面不计算) |
逻辑或 | 同上 | 逻辑或(前面表达式为true,后面不计算) |
break 中断并跳出当前循环。直接跳出当前的循环,从当前循环外面开始执行,忽略循环体中任何其余语句和循环条件测试。他只能跳出一层循环,若是你的循环是嵌套循环,那么你须要按照你嵌套的层次,逐步使用break来跳出.
return 致使当前的方法退出,并返回那个值。
选择因子:整数类型(long除外),enum(jdk1.5后),String(jdk1.7后)
字符串的switch是经过equals()和hashCode()方法来实现的。
注意break
类的加载包括:加载,验证,准备,解析,初始化
构造方法内能够用this调用另外一个构造方法
同名不一样参数为重载,与返回值类型无关;(好比你调用这个方法的时候java怎么根据返回值来区分你实际调用的方法)与访问修饰符无关
子类覆写父类的方法为重写
题型:怎么算重载和重写,重写的修饰符范围
final不能重写
名称 | 功能 |
---|---|
包访问权限 | 不提供任何权限修饰词状况下;默认包:处于同一个文件目录下 |
public | 均可用 |
protected | 修饰方法和成员变量,处理继承的概念,保证了仅有继承的类才能访问这个成员。同时具备包访问权限。 |
private | 仅在被包含的类里面可用,类的变量一般声明为private |
❤class编写顺序可按照:public👉protected👉private
类:❌(private & protected),⭕️(包访问权限 or public)
静态变量会默认赋初值,局部变量和final声明的变量必须手动赋初值
将对象置于新类中便可
想在新类中使用现有类的功能而非它的接口
构造函数不继承,只能显式(super)或者隐式的调用,导出类的构造器会插入对基类构造器的调用
方法(包括私有)均继承,可是不能直接调用私有方法。
ps:也可使用super访问父类被子类隐藏的变量或覆盖的方法。
将一个成员对象置于构造的类中,同时在新类中暴露了该成员的全部方法。
例:代理类中建立原类的对象,代理类的方法中调用此对象的方法。(其实就是套个壳,间接调用原类的方法)
初始化:一、定义的时候初始化
二、构造器初始化
三、惰性初始化(在使用这些对象以前)
四、实例初始化
只有命令行调用的main方法会被调用
确保正确清理:使用finally语句保证清理/执行类的全部特定清理动做,其顺序同生成顺序相反/不要使用finalize()
组合和继承(has a & is a ):
组合一般适用于在新类中使用现有类的功能而非接口,即在新类中嵌入一个现有类的private对象。
继承一般用于,使用某个类,并开发一个它的特殊版本。是否须要重新类向基类进行向上转型
数据(数值不变)
引用(引用不变可是对象自身能够改变)
❌数组中的数据没法被修饰为不可更改
空白final:被声明为final可是没有给定初始值的域,容许可是必须在域的定义处或者每一个构造器中对final赋值
final 的方法不能被修改(继承类中不能覆盖),final类不能继承(里面的方法也是final的,数值不受影响)
private final = private修饰的方法,继承类中能覆盖(由于方法为private 的时候,它就不是接口的一部分,导出类中相同名称的方法并非覆盖而是从新生成了一个新的方法);
用来修饰成员变量和成员方法,也能够造成静态static代码块。不能在方法中定义
static变量在第一次使用的时候初始化,只会有一份成员对象。
总结:(静态变量、静态初始化块)>(变量、初始化块)>构造器
针对非静态方法是编译看左,运行看右,可是对于成员变量,都是看左,也就是父类
它一般只是为了方便输出,好比System.out.println(xx),括号里面的“xx”若是不是String类型的话,就自动调用xx的toString()方法
Java中除了static和final方法之外,其余方法都是后期绑定(动态绑定)实现多态
动态绑定仅仅针对类的方法
一、抽象的方法没有body
二、含有抽象方法的类就是抽象类
三、不能建立实例
ps:抽象类的继承类,若是没有为全部方法提供方法体,编译器会强制用abstract来修饰这个类。
JDK 1.8之前,抽象类的方法默认访问权限为protected
JDK 1.8时,抽象类的方法默认访问权限变为default
只有public、static、final变量和抽象方法
能够在此以前加public,implements关键字,必须实现全部方法
不能建立实例
在jdk1.8以前,interface之中能够定义常量和方法,常量必须是public、static、final的,方法必须是public、abstract的。因为这些修饰符都是默认的
JDK8及之后,容许咱们在接口中定义static方法和default方法,有方法体
静态方法,只能经过接口名调用,不能够经过实现类的类名或者实现类的对象调用。
default方法,只能经过接口实现类的对象来调用。能够被覆写,可是不能再加default修饰符。
JDK 1.9时,接口中的方法能够是private的
接口的修饰符,有无body,接口和抽象类的区别等
一、不能有static变量和字段
二、尾部有分号
三、传入的参数是final
四、不含命名构造器,实例初始化的实际效果就是构造器
五、只能实现一个接口,且不能同时继承和实现接口
局部内部类:在方法体里面建立
须要不止一个该内部类的时候使用。局部内部类含有构造器,匿名内部类只能用于实例初始化
一、不能有static变量和字段
二、构造器必须链接到指向其外围对象的引用
三、外围类继承的时候,内部类并无被继承。即便基类和导出类的内部类名称相同也是不一样且独立的类。
经常使用:外部类有个方法指向内部类的引用(匿名类把内部类直接放到方法里面)
内部类对外部类引用:外部类.this(嵌套类没有)
建立内部类的对象:外部类对象.new 内部类名()
static的内部类
能够做为接口的一部分
静态内部类能够访问外围类的静态数据,包括私有数据,但不能访问非静态数据;
非静态内部类能够直接访问外围类的数据,包括私有数据
方法 | 功能 |
---|---|
Collections.synchronizedXxx() | 将指定的集合包装成线程同步的集合 |
collection1.containsAll(Collection2) | 合并两个collection |
Collection.addAll(collection,11,12,13,14) | 接受一个collection对象,以及一个数组或者是用逗号分割的列表 |
Collections.sort(list); | 根据元素的天然顺序 对指定列表按升序进行排序。 |
Collections.shuffle(List<?> list, Random rand); | 打乱 |
arrays.aslist()接受一个逗号分隔的元素列表(可变参数)或者数组,并转换为一个list对象
表明长度能够改变得数组。能够对元素进行随机的访问,插入与与删除元素的速度慢。
默认大小为10,不够再以1.5倍扩容新数组,拷贝数据到新数组中
在实现中采用链表数据结构。插入和删除速度快,随机访问速度慢。
method | 解释 |
---|---|
subList(List2) | 建立片断 |
indexOf(object) | 获取索引编号 |
containsAll(List2) | 是否包含,无视顺序 |
addFirst/add/addLast | 添加单个元素 |
remove/removeFirst/poll/移除并返回列表头 removeLast 移除并返回列表尾 | |
getFirst/element/peek | |
peek/poll 列表空返回null | |
详细参考:LinkedList基本用法
一个是数组,一个是链表,二者的读写属性基本相反。
单向移动:
while(it.hasNext()) { Pet p = it.next(); print(p); }
延伸点:可能会引发ConcurrentModificationException 异常
ListIterator:双向移动
method | 解释 |
---|---|
nextIndex() | 返回列表中ListIterator所需位置后面元素的索引 |
previousIndex() | |
previous() | |
next() | |
listIterator(n) | 一开始就指向列表索引为n处的ListIterator,空则指向开始处 |
remove() | 对迭代器使用hasNext()方法时,删除ListIterator指向位置后面的元素;当对迭代器使用hasPrevious()方法时,删除ListIterator指向位置前面的元素 |
不重复,测试归属性(查找是否存在set中)
和collection接口相同
Map没有继承于Collection接口
Map集合中的键对象不容许重复,也就说,任意两个键对象经过equals()方法比较的结果都是false,可是能够将任意多个键独享映射到同一个值对象上。
Object put(Object key, Object value):
Object remove(Object key)
void putAll(Map t): 未来自特定映像的全部元素添加给该映像
void clear(): 从映像中删除全部映射
Object get(Object key): 得到与关键字key相关的值
containsKey()和containsValue()测试Map中是否包含某个“键”或“值”。
名称 | 线程安全 | 功能 |
---|---|---|
HashMap | 不安全 | 基于散列表的实现。插入和查询“键值对”的开销是固定的。能够经过构造器设置容量capacity和负载因子load factor,以调整容器的性能。 |
LinkedHashMap | 不安全 | 相似于HashMap,可是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,由于它使用链表维护内部次序。 |
TreeMap | 不安全 | 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特色在于,你获得的结果是通过排序的。TreeMap是惟一的带有subMap()方法的Map,它能够返回一个子树。 |
WeakHashMap | 不安全 | 弱键(weak key)Map,Map中使用的对象也被容许释放: 这是为解决特殊问题设计的。若是没有map以外的引用指向某个“键”,则此“键”能够被垃圾收集器回收。 |
IdentifyHashMap | 不安全 | 使用==代替equals()对“键”做比较的hash map。专为解决特殊问题而设计。 |
ConcurrentHashMap | 安全 | 将Hash表分为16segment,每次只须要的segment进行加锁 |
它根据键的hashCode值存储数据,大多数状况下能够直接定位到它的值,于是具备很快的访问速度,但遍历顺序倒是不肯定的。
HashMap 大概具备如下特色:
除了不容许 null 而且同步,Hashtable 几乎和他同样。
JDK1.7 中hashmap 是经过 桶(数组)加链表的数据结构来实现的。当发生hash碰撞的时候,以链表的形式进行存储。
JDK1.8 中hashmap 增长了在原有桶(数组) + 链表的基础上增长了黑红树的使用。当一个hash碰撞的数量超过指定次数(TREEIFY_THRESHOLD)的时候,链表将转化为红黑树结构。
默认为0.75。通常不建议修改,
桶数默认为16,很是规设计(通常采用素数),主要是为了在取模和扩容时作优化,同时为了减小冲突。
关于桶数,在构造hashmap时,若是你传入的是一个本身设定的数字,它会调用tableSizeFor帮你转换成一个比传入容量参数值大的最小的2的n次方(因此无论你传入什么数字最终仍是2的幂)。
这里tableSizeFor若是你看过源代码的话,会发现用的是挺晦涩的位运算(强烈吐槽),那一大堆的运算你能够理解为把这个数字的全部位都变为1,再加上1,就能获得比传入容量参数值大的最小的2的n次方。
默认值为16应该是
HashMap 中经过将传入键的 hashCode
和此值按位右移 16 位补零后的值
进行按位异或,获得这个键的哈希值。
(h = key.hashCode()) ^ (h >>> 16);
这里是因为,因为哈希表的容量都是 2 的 N 次方(最大2^30),在当前,元素的 hashCode() 在不少时候下低位是相同的,这将致使冲突(碰撞),所以 1.8 之后作了个移位操做:将元素的 hashCode() 和本身右移 16 位后的结果求异或。
因为 int 只有 32 位(4*8),无符号右移 16 位至关于把高位的一半移到低位:
使用segment来分段和管理锁,segment继承自ReentrantLock,所以ConcurrentHashMap使用ReentrantLock来保证线程安全。
JDK1.8以前采用分段锁,核心就是一句话:尽可能下降同步锁的粒度。
JDK1.8以后使用CAS思想代替冗杂的分段锁实现。
不出意料,面试者答出CAS以后一定会被追问其思想以及应用,换作我本身的话会有以下思路做答:CAS采用乐观锁思想达到lock free,提一下sun.misc.Unsafe中的native方法,至于CAS的其余应用能够聊一聊Atomic原子类和一些无锁并发框架(如Amino),提到ABA问题加分。
都实现了Map接口,主要的区别有:
先进先出
喂(Vector) S(Stack) H(hashtable) E(enumeration)
抛InterruptedException的表明方法有:
- java.lang.Object 类的 wait 方法
- java.lang.Thread 类的 sleep、join方法
catch匹配到第一个符合的处理程序就结束
能够重写构造器,传入一个字符串
调用printStackTrace()能够打印出异常
构造器:派生类构造器不能捕获基类构造器抛出的异常/派生类构造器的异常说明必须包含基类构造器的异常说明。
若是基类构造器没有异常说明,派生类能有。
方法:派生类方法遵照基类方法的异常说明,可是也能够不抛出异常。
若是基类方法没有异常说明,派生类也不能有。
对象:派生类的对象只被要求捕获这个类所抛出的异常/基类的对象只被要求捕获基类所抛出的异常
异常匹配:抛出的异常能够被此异常的基类catch
因此使用try、catch、finally语句块中须要注意的是
String对象是不可变的(只读),内部使用 char 数组存储数据,该数组被声明为 final 。
方法 | 功能 |
---|---|
substring | 左闭右开,只有一个数值则右边到底 |
int indexOf(String str) | 可用于判断子字符串是否存在 |
Java 5提出,因为String的链接(+)实际上是调用了StringBuilder.append,因此,建议在for循环内使用StringBuilder来进行字符串的拼接。
和StringBuilder相似
若是程序是在单线程下运行,或者是没必要考虑到线程同步问题,咱们应该优先使用StringBuilder类;若是要保证线程安全,天然是StringBuffer。
优先StringBuilder,为了线程安全用StringBuffer
String正则表达式相关的method
method | 解释 |
---|---|
matches(String regex) | |
split(String regex) | |
split(String regex, int limit) | 限制分割次数,最终数组数为limit |
replaceFirst(),replaceAll() |
如:String str="abcaxc"; Patter p="ab*c";
贪婪匹配:正则表达式通常趋向于最大长度匹配,也就是所谓的贪婪匹配。如上面使用模式p匹配字符串str,结果就是匹配到:abcaxc(ab*c)。
非贪婪匹配:就是匹配到结果就好,就少的匹配字符。如上面使用模式p匹配字符串str,结果就是匹配到:abc(ab*c)。
Pattern p = Pattern.compile(regex); Matcher m = p.matcher(String args);
method | 解释 |
---|---|
matches() | 是否匹配 |
lookingAt() | 该字符串的起始部分是否匹配 |
find() |
返回参与比较的先后两个字符串的asc码的差值,
若是两个字符串首字母不一样,则该方法返回首字母的asc码的差值;
若是参与比较的两个字符串若是首字符相同,则比较下一个字符,直到有不一样的为止,返回该不一样的字符的asc码差值,若是两个字符串不同长,能够参与比较的字符又彻底同样,则返回两个字符串的长度差值
RTTI:runtime type information
延伸点:类的加载
全部的类都是在第一次使用时,动态加载到JVM中的。
构造器也是static
Class.forName(目标类全限定名的String),返回class引用,若是没有加载就加载它
Object.getClass。返回该对象的实际类型的Class引用
Class.getInterfaces
Class.getSuperclass
Class.newInstance:
一般状况下,一个编译器处理泛型有两种方式:Code specialization
和Code sharing
。C++和C#是使用Code specialization
的处理机制,而Java使用的是Code sharing
的机制。
Code sharing方式为每一个泛型类型建立惟一的字节码表示,而且将该泛型类型的实例都映射到这个惟一的字节码表示上。将多种泛型类形实例映射到惟一的字节码表示是经过类型擦除(type erasue
)实现的。
也就是说,对于Java虚拟机来讲,他根本不认识Map<String, String> map
这样的语法。须要在编译阶段经过类型擦除的方式进行解语法糖。
类型擦除的主要过程以下:
虚拟机中没有泛型,只有普通类和普通方法,全部泛型类的类型参数在编译时都会被擦除,泛型类并无本身独有的Class类对象。好比并不存在List<String>.class
或是List<Integer>.class
,而只有List.class
。
生成器(generator)专门负责创造对象的类;
通常一个生成器只定义一个方法:用以产生新的对象;
基本类型没法做为类型参数
泛型方法所在的类能够不是泛型类,若是static方法须要使用泛型能力,就必须使其成为泛型方法
使用泛型类,必须在建立对象的时候指定类型参数的值,泛型方法不用
类型推断避免重复的泛型参数列表,可是只对赋值有效:
e.g:Map<Person,List<? extends Pet>> petPeople = New.Map()
;
反射能够用于判断任意对象所属的类,得到Class对象,构造任意一个对象以及调用一个对象
详见:java中的反射机制
//获取类型 Class c1 = Class.forName("Employee"); Class c2 = Employee.class; Employee e = new Employee(); Class c3 = e.getClass(); //建立对象 Objecto = c1.newInstance(); //调用了Employee的无参数构造方法. //获取属性 Field[] fs = c.getDeclaredFields(); getDeclaredMethods()//获取全部的方法 getReturnType() //得到方法的放回类型 getParameterTypes() //得到方法的传入参数类型 getDeclaredMethod("方法名",参数类型.class,……)//得到特定的方法 getDeclaredConstructors()//获取全部的构造方法 getDeclaredConstructor(参数类型.class,……) //获取特定的构造方法 getSuperclass()//获取某类的父类 getInterfaces()//获取某类实现的接口
使用案例如Hibernate,BeanUtils。
在程序运行时,运用反射机制动态建立而成。
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。
java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
代理类每每会在代理对象业务逻辑先后增长一些功能性的行为,如使用事务或者打印日志。
必须实现InvocationHandler接口,并在该类中定义代理行为。
依赖于字节码技术
cglib是针对类来实现代理的,他的原理是对指定的目标类
生成一个子类
,并覆盖其中方法实现加强,但由于采用的是继承,因此不能对final修饰
的类进行代理。
数组命名时名称与[]能够随意排列
粗糙数组:构成矩阵的每一个向量均可以具备任意的长度
不能实例化具备参数化类型的数组
建立测试数据:
Arrays.asList()返回的是一个List (List是一个接口,返回List实际是返回List接口的一个实现),这个List在底层是有数组实现的,因此size是fixed的
Arrays.fill(Arrays,parameters):用同一个parameter填充整个数组(引用)
Arrays实用功能:
System.arraycopy(源数组,偏移量,目标组,复制偏移量,复制个数)
file既能够表明一个文件名,也能够表明一个文件夹的名字
bio,nio区别要熟知,了解nio中的ByteBuffer,Selector,Channel能够帮助面试者度过很多难关。几乎提到nio一定会问netty,其实我分析了一下,问这个的面试官本身也不必定会,但就是有人喜欢问,因此我们适当应付一下就好:一个封装很好扩展很好的nio框架,经常使用于RPC框架之间的传输层通讯。
后缀是stream的都是字节流,其余的都是字符流
声明为static和transient类型的成员数据不能被串行化。由于static表明类的状态, transient表明对象的临时数据。
inputstream的close方法用来关闭流
skip()用来跳过一些字节
mark()用来标记流
reset()复位流
一、缓冲输入文件
缓冲区的做用的主要目的是:避免每次和硬盘打交道,提升数据访问的效率。
public class BufferedInputFile { public static String read(String filename) throws IOException { BufferedReader in = new BufferedReader(new FileReader(filename)); String s; StringBuilder sb = new StringBuilder(); while((s = in.readLine())!= null) sb.append(s + "\n"); in.close(); return sb.toString(); } public static void main(String[] args)throws IOException { System.out.print(read("BufferedInputFile.java")); } }
二、从内存输入
public class MemoryInput { public static void main(String[] args)throws IOException { StringReader in = new StringReader(BufferedInputFile.read("MemoryInput.java")); int c; //read()是以int形式返回下一字节,必须转为char才能正确打印 while((c = in.read()) != -1) System.out.print((char)c); } }
三、格式化的内存输入
public class FormattedMemoryInput { public static void main(String[] args) throws IOException { try { DataInputStream in = new DataInputStream(new ByteArrayInputStream( BufferedInputFile.read("FormattedMemoryInput.java").getBytes())); while(true) System.out.print((char)in.readByte()); } catch(EOFException e) { System.err.println("End of stream"); } } }
四、基本的文件输出
public class BasicFileOutput { static String file = "BasicFileOutput.out"; public static void main(String[] args)throws IOException { BufferedReader in = new BufferedReader( new StringReader(BufferedInputFile.read("BasicFileOutput.java"))); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file))); int lineCount = 1; String s; while((s = in.readLine()) != null ) out.println(lineCount++ + ": " + s); out.close(); // Show the stored file: System.out.println(BufferedInputFile.read(file)); } }
为了提升读写性能,能够采用什么流
Java中有几种类型的流
JDK 为每种类型的流提供了一些抽象类以供继承,分别是哪些类
对文本文件操做用什么I/O流
对各类基本数据类型和String类型的读写,采用什么流
能指定字符编码的 I/O 流类型是什么
延伸点:装饰者模式
BIO:同步阻塞式IO,简单理解:一个链接一个线程;Apache,Tomcat。主要是并发量要求不高的场景
NIO:同步非阻塞IO,简单理解:一个请求一个线程;Nginx,Netty。主要是高并发量要求的场景
AIO:异步非阻塞IO,简单理解:一个有效请求一个线程;还不是特别成熟,底层也基本是多线程模拟,因此应用场景很少,Netty曾经用了,但又放弃了。
什么是序列化?如何实现 Java 序列化及注意事项
Serializable 与 Externalizable 的区别
方法 | 功能 | 返回值 |
---|---|---|
values() | 返回enum实例的数组 | T[] |
compareTo(E o) | 顺序比较,若是该枚举对象位于指定枚举对象以后,则返回正整数;反之返回负整数;不然返回零; | int |
name() | 返回此枚举实例的名称 | |
getDeclaringClass() | 返回实例所属于的enum类 | T |
ordinal() | 返回实例声明时候的顺序 | int |
定义实例方法必须在任何方法或者属性前
定义实例方法后面加上“;”,而后开始写方法
当多个线程访问某个类时,无论运行时环境采用何种调度方式或者这些线程将如何交替进行,而且在主调代码中不须要任何额外的同步或协同,这个类都能表现出正确的行为,那么称这个类是线程安全的
一般与锁一块儿出现:除了synchronized以外,还常常被问起的是juc中的Lock接口,其具体实现主要有两种:可重入锁,读写锁。这些都没问题的话,还会被询问到分布式下的同步锁,通常借助于中间件实现,如Redis,Zookeeper等,开源的Redis分布式锁实现有Redisson,回答注意点有两点:一是注意锁的可重入性(借助于线程编号),二是锁的粒度问题。除此以外就是一些juc的经常使用工具类如:CountdownLatch,CyclicBarrir,信号量
ExecutorService exec = Executors.newCacheThreadPool(); for(int i ; i <5 ; i++) //Liftoff实现了runnable接口 exec.execute(new Liftoff()); //防止新任务提交给这个executor exec.shutdown();
newFixedThreadPool(int number);一次性预先执行高代价的线程分配
newSingleThreadExecutor; 相似于newFixedThreadPool(1),会序列化全部提交给它的任务。
method | 解释 |
---|---|
run() | 执行线程体中具体的内容,若是直接调用run方法,其实仍是单线程 |
start() | 启动线程对象,使其进入就绪状态 |
sleep() | 占着CPU不工做。能够抛出interruptException,可是异常抛出的时候回清理这个标志,致使在catch里面调用isInterrupt(),总为false。TimeUnit.SECONDS.sleep(1):可读性更高 |
suspend() | 使线程挂起,要经过resume()方法使其从新启动 |
join() | 等待该线程终止;在父线程中,子线程调用join,父线程被挂起,等待子线程结束才回复。 |
yield() | 当前线程放弃CPU,全部线程(包括当前线程)共同竞争CPU。 |
setDeamon(true) | 设置为后台线程,必须在线程start以前调用。 |
getName() | 有一构造函数参数为String name;获取线程的name。 |
isAlive() | 判断是否被挂起 |
holdsLock() | 返回true若是当且仅当当前线程拥有某个具体对象的锁。 |
wait:等待使用CPU
sleep:占着CPU不工做
延伸点:注意
interrupted()
会清楚中断状态参考连接:如何正确地中止一个线程?
调度器倾向于让优先级最高的线程先执行,一般在run的开头部分设定
getPriority():读取现有线程的优先级
setPriority():修改线程优先级。
每一个线程都有一个ThreadLocal就是每一个线程都拥有了本身独立的一个变量,竞争条件被完全消除了。
它是为建立代价高昂的对象获取线程安全的好方法,好比你能够用ThreadLocal让SimpleDateFormat变成线程安全的,由于那个类建立代价高昂且每次调用都须要建立不一样的实例因此不值得在局部范围使用它,若是为每一个线程提供一个本身独有的变量拷贝,将大大提升效率。
首先,经过复用减小了代价高昂的对象的建立个数。其次,你在没有使用高代价的同步或者不变性的状况下得到了线程安全。线程局部变量的另外一个不错的例子是ThreadLocalRandom类,它在多线程环境中减小了建立代价高昂的Random对象的个数。
悲观锁是独占锁,阻塞锁,写入频繁使用悲观锁
乐观锁是非独占锁,非阻塞锁。读取频繁使用乐观锁。
乐观锁(
Optimistic Locking
)实际上是一种思想。相对悲观锁而言,乐观锁假设认为数据通常状况下不会形成冲突,因此在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,若是发现冲突了,则让返回用户错误的信息,让用户决定如何去作。有一种乐观锁的实现方式就是CAS ,这种算法在JDK 1.5中引入的
java.util.concurrent
(J.U.C)中有普遍应用。可是值得注意的是这种算法会存在ABA问题(部分经过版本号来解决)
这里就是悲观锁乐观锁的应用处理并发问题
参考资料:乐观锁的一种实现方式——CAS
死锁是指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。这是一个严重的问题,由于死锁会让你的程序挂起没法完成任务。
死锁的发生必须知足如下四个条件:
避免死锁最简单的方法就是阻止循环等待条件,将系统中全部的资源设置标志位、排序,规定全部的进程申请资源必须以必定的顺序(升序或降序)作操做来避免死锁。
Q:如何检查死锁?
A:经过jConsole检查死锁
参考资料:java 查看线程死锁
可重入锁,也叫作递归锁,指的是同一线程 外层函数得到锁以后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁
加到 static 方法上是给 Class 上锁,即整个类上锁。
Lock对象必须被显式地建立、锁定和释放。
紧接着lock的调用,后面是try-finally语句,同时finally里面要有unclock来释放锁
public class MutexEvenGenerator extends IntGenerator { private int currentEvenValue = 0; private Lock lock = new ReentrantLock(); public int next() { lock.lock(); try { ++currentEvenValue; Thread.yield(); // Cause failure faster ++currentEvenValue; return currentEvenValue; } finally { lock.unlock(); } } public static void main(String[] args) { EvenChecker.test(new MutexEvenGenerator()); } } ///:~
禁止指令重排序优化。编译器只保证程序执行结果与源代码相同,却不保证明际指令的顺序与源代码相同。这在单线程看起来没什么问题,然而一旦引入多线程,这种乱序就可能致使严重问题。volatile关键字就能够从语义上解决这个问题。 jdk1.5之后生效。
关注点:主存和工做内存, happens-before原则(主要是前三条)
JMM规定全部变量都是存在主存中的,每一个线程又包含本身的工做内存。因此线程的操做都是以工做内存为主,它们只能访问本身的工做内存,且工做先后都要把值在同步回主内存。
基本类型变量,除了long和double,都能保证原子性的操做。
long和double因为是分离的32位来操做,不能保证原子性。
当一个变量被volatile修饰时,那么对它的修改会马上刷新到主存,当其它线程须要读取该变量时,会去内存中读取新值。而普通变量则不能保证这一点。
其实经过synchronized和Lock也可以保证可见性,线程在释放锁以前,会把共享变量值都刷回主存,可是synchronized和Lock的开销都更大。
JMM是容许编译器和处理器对指令重排序的,可是规定了as-if-serial语义,即无论怎么重排序,程序的执行结果不能改变。
ThreadB_start()
(启动线程B) , 那么A线程的ThreadB_start()
happens-before 于B中的任意操做ThreadB.join()
而且成功返回,那么线程B中的任意操做happens-before于线程A从ThreadB.join()
操做成功返回。interrupt()
方法的调用先行发生于被中断线程代码检测到中断事件的发生,能够经过Thread.interrupted()
方法检测是否有中断发生finalize()
方法的开始同步器是一些使线程可以等待另外一个线程的对象,容许它们协调动做。最经常使用的同步器是CountDownLatch和Semaphore,不经常使用的是Barrier 和Exchanger
延伸点:java多线程面试汇总
要注意true,false,null, friendly,sizeof不是java的关键字,可是你不能把它们做为java标识符用。
null是表明不肯定的对象,null是一个关键字。所以能够将null赋给引用类型变量,但不能够将null赋给基本类型变量。
容器类型与null
List:容许重复元素,能够加入任意多个null。
Set:不容许重复元素,最多能够加入一个null。
Map:Map的key最多能够加入一个null,value字段没有限制。
数组:基本类型数组,定义后,若是不给定初始值,则java运行时会自动给定值。引用类型数组,不给定初始值,则全部的元素值为null。
一、判断一个引用类型数据是否null。 用==来判断。
二、释放内存,让一个非null的引用类型变量指向null。这样这个对象就再也不被任何对象应用了。等待JVM垃圾回收机制去回收。
java.lang包定义了一些基本的类型,包括Integer,String之类的,是java程序必备的包,有解释器自动引入,无需手动导入
工具 | 功能 |
---|---|
javac.exe | 把.java文件编译为.class文件(每一个类一个.class文件) |
java.exe | 执行编译好的.class文件 |
javadoc.exe | 生成Java说明文档,api文档 |
jdb.exe | Java调试器 |
javaprof.exe | 剖析工具 |
Java语言使用的是Unicode字符集。
ascii:美国标准信息交换码。使用的是1个字节的7位来表示该表中的字符。
ISO8859-1:拉丁码表。使用1个字节来表示。
GB2312:简体中文码表。
GBK:简体中文码表,比GB2312融入更多的中文文件和符号。
unicode:国际标准码表。都用两个字节表示一个字符。
UTF-8:对unicode进行优化,每个字节都加入了标识头。
名称 | 功能 |
---|---|
单一职责原则(Single-Resposibility Principle) | 一个类,最好只作一件事,只有一个引发它的变化。单一职责原则能够看作是低耦合、高内聚在面向对象原则上的引伸,将职责定义为引发变化的缘由,以提升内聚性来减小引发变化的缘由。 |
开放封闭原则(Open-Closed principle) | 软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。 |
Liskov替换原则(Liskov-Substituion Principle) | 子类必须可以替换其基类。这一思想体现为对继承机制的约束规范,只有子类可以替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。 |
依赖倒置原则(Dependecy-Inversion Principle) | 依赖于抽象。具体而言就是高层模块不依赖于底层模块,两者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。 |
接口隔离原则(Interface-Segregation Principle) | 使用多个小的专门的接口,而不要使用一个大的总接口 |
session.setAttribute()和session.getAttribute()配对使用,做用域是整个会话期间,在全部的页面都使用这些数据的时候使用。
request.getAttribute()表示从request范围取得设置的属性,必需要先setAttribute设置属性,才能经过getAttribute来取得,设置与取得的为Object对象类型。
延伸点:JDK, JRE 和JVM的区别
继承自Hashtable
我本身实际使用应该只有在xml里
<context:property-placeholder location="classpath*:conf/conf_a.properties"/> <bean class="com.xxx.aaa.Bean1" p:driverClassName="${modulea.jdbc.driverClassName}" p:url="${modulea.jdbc.url}" p:username="${modulea.jdbc.username}" p:password="${modulea.jdbc.password}"/>
兼容数组类参数的。
必须做为参数列表的最后一项
能匹配定长的方法,那么优先匹配该方法。含有不定参数的那个重载方法是最后被选中的。
参考: Java方法的可变参数个数