本文是Android面试题整理中的一篇,结合右下角目录食用更佳,包括:html
面向对象编程(Object Oriented Programming)java
- JDK:java development kit:java开发工具包,是开发人员所须要安装的环境(包含JRE)
- JRE:java runtime environment:java运行环境,java程序运行所须要安装的环境
面向对象的特征有:抽象、封装、继承、多态面试
- 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
- 封装:隐藏对象的实现细节,仅对外公开接口,是针对一个对象来讲的
- 多态:多态性是指容许不一样子类型的对象对同一消息做出不一样的响应。简单的说就是用一样的对象引用调用一样的方法可是作了不一样的事情
- 继承:继承是从已有类获得继承信息建立新类的过程。提供继承信息的类被称为父类(超类、基类);获得继承信息的类被称为子类(派生类)
java是值传递。能够理解为传入的是一个引用的副本,指向统一地址。当值改变时,原引用和副本指向地址中的值都变了;当副本指向的地址改变,指向新值时,原引用指向的地址没有改变,原值也没有改变。算法
- 规范
- 扩展
- 回掉
- java是单继承的
- 为其余子类提供一个公共的类型
- 封装子类中重复定义的内容
- 定义抽象方法,子类能够有不一样的实现
- 单继承:java中只能够继承一个类,可是能够实现多个接口
- 成员变量:接口的成员变量都是public static final 的,抽象类能够有各类类型
- 方法:抽象类中能够有方法的具体实现,接口中方法都是抽象的
- 扩展://jdk 7 : 只能声明全局常量(public static final)和抽象方法(public abstract) void method1(); // jdk 8 : 声明静态方法 和 默认方法 public static void method2(){ System.out.println("method2"); } default void method3(){ System.out.println("method3"); method4(); } //jdk 9 : 声明私有方法 private void method4(){ System.out.println("私有方法"); }}
- 接口能够继承接口,并且支持多重继承
- 抽象类能够实现(implements)接口
- 抽象类是否可继承具体类,也能够继承抽象类
- 数字、字母、下划线、$(java中内部类编译后会生成包含$的类名) 组成
- 不能以数字开头
- 不能和关键字或保留关键字相同
- 方法:java中一般用小驼峰命名法
- 常量:一般用大写字母,不一样单词间用“_”分隔开,如MOBILE_NUM
- 类名:大驼峰命名法
一个".java"文件内能够有多个类,但只能有一个类是公开的数据库
构造器不能被继承,所以不能被重写,但能够被重载编程
- 静态变量属于类,被多个实例共享,成员变量属于实例
- 静态变量储存在方法区,成员变量在堆
- 静态变量在类加载时候存在,成员变量在实例加载以后存在
- 静态方法能够直接使用静态变量,不能直接使用成员变量
clone/toString/wait/notify/notifyAll/equals/hashcode/finalize/getClass设计模式
- Cloneable是一个接口,没有具体方法
- clone方法是Object类中方法,会检查当前实例是否实现Cloneable接口,没有实现则抛出异常,实现了就调用native方法进行clone(clone进行的是浅拷贝),源码以下
protected Object clone() throws CloneNotSupportedException {
if (!(this instanceof Cloneable)) {
throw new CloneNotSupportedException("Class " + getClass().getName() +
" doesn't implement Cloneable");
}
return internalClone();
}
复制代码
- 不对。
- java 规定,值相同,hashCode必定要相同;hashCode相同,值可能不一样
- 若是值相同,hashCode不一样,就会形成Hashset、HashMap等借助hashCode实现的数据结构出现错乱,相同的值或者key可能出现屡次
- 经过实现Cloneable接口实现clone:这里要注意深拷贝和浅拷贝问题,若是该类内部变量是引用类型的,而且内部变量类没有实现Cloneable接口,那么克隆出来的该变量是浅拷贝的(只是复制了引用,两个引用指向统一实例)
- 经过实现Serializable接口,经过对象的序列化和反序列化实现克隆。
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class MyUtil {
private MyUtil() {
throw new AssertionError();
}
@SuppressWarnings("unchecked")
public static <T extends Serializable> T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
// 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义
// 这两个基于内存的流只要垃圾回收器清理对象就可以释放资源,这一点不一样于对外部资源(如文件流)的释放
}
}
复制代码
- == :对于基本类型,比较的是他们的值;对于引用类型,比较的是引用的值,也就是对象实例的地址
- equals()方法是Object类中的方法,默认实现是public boolean equals(Object obj) {return (this == obj);};咱们能够重写该方法达到咱们的目的,例如String重写了该方法要求每一个字符都相等。
指出下面程序的运行结果数组
class A {
static {
System.out.print("1");
}
public A() {
System.out.print("2");
}
}
class B extends A{
static {
System.out.print("a");
}
public B() {
System.out.print("b");
}
}
public class Hello {
public static void main(String[] args) {
A ab = new B();
ab = new B();
}
}
复制代码
执行结果:1a2b2b。 建立对象时构造器的调用顺序是: 父类静态初始化块 -> 子类静态初始化块 -> 父类初始化块 ->调用了父类构造器 -> 子类初始化块 -> 调用子类的构造器缓存
重写(Override)和重载(Overload)其实并没有联系,多是由于名称类似,容易引发混淆 > 重写发生在运行时,重载发生在编译期安全
重写是针对父类和子类来讲的,是在子类中重写父类的方法。
- 要求方法名,参数个数和类型必须相同
- 返回的数据类型必须与父类相同或者是其子类
- 访问修饰符的限制必定要大于父类中该方法的访问修饰符(public>protected>default>private)
- 重写方法必定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
重载是针对一个类说的,是Java中多态性的一种表现
- 要求方法名相同
- 必须有不一样的参数列表
- 能够有不一样的返回类型
- 能够有不一样的修饰符
- 能够抛出不一样的异常。
扩展:华为的面试题中曾经问过这样一个问题 - "为何不能根据返回类型来区分重载"
答:由于调用时不能指定类型信息,编译器不知道你要调用哪一个函数。例如:
float max(int a, int b);
int max(int a, int b);
当调用max(1, 2);时没法肯定调用的是哪一个。
参考:https://www.zhihu.com/question/21455159/answer/59874307
复制代码
- 静态变量(static 修饰的变量)属于类,被全部类的实例共享,没有实例时也可经过类直接访问
- 实例变量:必须经过实例来访问
不能够,静态方法只能访问静态成员,由于非静态方法的调用要先建立对象,在调用静态方法时可能对象并无被初始化
- 抽象方法不能是静态的:静态方法不能被子类重写,抽象方法必须被子类重写,冲突;
- 抽象方法不能是native的:本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的
- 抽象方法不能用sychronized:synchronized和方法的实现细节有关,抽象方法不涉及实现细节,所以也是相互矛盾的
Super表示当前类的父类对象;This表示当前类的对象
- equals 相等,hashcode必定相等
- hashcode相等,equals不必定相等
- 内部类能够很好的实现隐藏
- 内部类拥有外围类的全部元素的访问权限
- 能够间接实现多重继承
- 能够避免修改接口而实现同一个类中两种同名方法的调用
- 内部类持有外部类的引用(this),静态内部类不持有
- 由于持有外部类的引用,因此new时须要先有外部类的实例,再用外部类实例new内部类实例,举例:new Outer().new Inner();
- 扩展:在Android中,由于内部类持用外部类引用,因此容易形成内存泄漏,通常推荐使用静态内部类
能够继承其余类,也能够实现接口
解析:
btn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View view){
}
})
这里new的就是一个匿名内部类,这个匿名内部类实现了View.OnClickListener接口。因此匿名内部类自己必定会继承或实现一个且仅一个类或者接口。
复制代码
- 一个内部类对象能够访问建立它的外部类对象的成员,包括私有成员。
- 应用局部变量,局部变量前要加final修饰
- 内部类和局部变量生命周期不一样(方法结束后局部变量的生命周期就结束了,而内部类只要有引用就不结束,内部类的生命周期>=局部变量)
- Java为了解决这一问题,会在编译时在内部类的构造方法里边,将局部变量做为参数传入内部类
- 这就形成了局部变量若是改变,内部类不知情的场景,因此要加final,保证引用不可改变
扩展:在java8中,能够不使用final关键字,可是若是咱们改变局部变量的引用,编译会发生错误,从而保证了局部变量的引用不变。
内部类虽然和外部类写在同一个文件中, 可是编译完成后, 仍是生成各自的class文件,内部类经过this访问外部类的成员。
- 编译器自动为内部类添加一个成员变量, 这个成员变量的类型和外部类的类型相同, 这个成员变量就是指向外部类对象
- 编译器自动为内部类的构造方法添加一个参数, 参数的类型是外部类的类型, 在构造方法内部使用这个参数为内部类中添加的成员变量赋值;
- 在调用内部类的构造函数初始化内部类对象时,会默认传入外部类的引用。
- 基类是Throwable,Error和Exception继承自Throwable
- Error一般是系统抛出来的,也能够catch到,但通常不可恢复,开发是也不作处理
- Exception分为受检查异常和不受检查异常,受检查异常会在编译时强制要求咱们try/catch
- throw:抛出异常
- throws:在方法声明处使用,表示此方法可能抛出的异常,调用此方法处须要处理这些异常。
- Error是系统抛出的,不能在运行时捕获,好比内存溢出
- Exception 是须要咱们捕捉并处理的异常,如类型转换错误等,咱们能够经过捕捉异常,使程序发生异常时仍可正常运行
- checked exception:这种异常,JAVA编译器强制要求咱们必需对出现的这些异常进行try/catch或者继续上抛
- runtime exception:出现运行时异常后,系统会把异常一直往上层抛,一直遇处处理代码。若是没有处理块,到最上层,若是是多线程就由Thread.run()抛出,若是是单线程就被main()抛出。抛出以后,若是是线程,这个线程也就退出了。若是是主程序抛出的异常,那么这整个程序也就退出了
NullPointerException (空指针异常) ClassCastException (类转换异常) IndexOutOfBoundsException (下标越界异常) IllegalArgumentException (非法参数异常)
题目1:
类ExampleA继承Exception,类ExampleB继承ExampleA。有以下代码片段,请问执行此段代码的输出是什么?
try {
throw new ExampleB("b")
} catch(ExampleA e){
System.out.println("ExampleA");
} catch(Exception e){
System.out.println("Exception");
}
解析:ExampleA。(根据里氏代换原则[能使用父类型的地方必定能使用子类型],抓取ExampleA类型异常的catch块可以抓住try块中抛出的ExampleB类型的异常)
题目2:
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
class Human {
public static void main(String[] args)
throws Exception {
try {
try {
throw new Sneeze();
}
catch ( Annoyance a ) {
System.out.println("Caught Annoyance");
throw a;
}
}
catch ( Sneeze s ) {
System.out.println("Caught Sneeze");
return ;
}
finally {
System.out.println("Hello World!");
}
}
}
解析:输出Caught AnnoyanceCaught SneezeHello World!
复制代码
- &有两种用法:(1)按位与;(2)逻辑与,咱们这里说的是逻辑与。
- 与运算要求左右两端的布尔值都是true整个表达式的值才是true
- &&运算符是短路逻辑与运算,若是&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算
- &左右两边的表达式都会计算,咱们经常使用&&,好比if(username != null &&!username.equals("hahaha")){}
若是用transient声明一个实例变量,当对象存储时,它的值不须要维持。换句话来讲就是,用transient关键字标记的成员变量不参与序列化过程
修饰符一共有四个:private、protected、public和default(也有人管默认叫friendly)
- private:私有的,除本身外任何类不能使用
- protected:同包可使用,其余包子类可使用
- public:任何类可使用
- default:同包可使用,其余包不能使用
修饰符 | 当前类 | 同 包 | 子 类 | 其余包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
goto 和 const 是Java中的保留字,在目前版本的Java中没有使用。
在最外层循环前加一个标记如A,而后用break A;能够跳出多重循环 (应该避免使用带标签的break和continue,由于它不会让你的程序变得更优雅)。
- 在Java 5之前,switch(expr)中,expr只能是byte、short、char、int
- Java 5开始,Java中引入了枚举类型,expr也能够是enum类型
- 从Java 7开始,expr还能够是字符串(String)
- long类型不支持
- 能够修饰内部类(静态内部类)
- 能够修饰成员变量,该变量属于类,被全部实例共享
- 能够修饰方法,该方法属于类,被全部实例共享
- 能够修饰代码块(静态代码块),该代码块在第一次被加载时被调用
- Java经过面向对象的方法进行异常处理,把各类不一样的异常进行分类在Java中,每一个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法能够捕获到这个异常并能够对其进行处理。
- Java的异常处理是经过5个关键词来实现的:try、catch、throw、throws和finally。通常状况下是用try来执行一段程序,若是系统会抛出(throw)一个异常对象,能够经过它的类型来捕获(catch)它,或经过老是执行代码块(finally)来处理;try用来指定一块预防全部异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各类异常(固然声明异常时容许无病呻吟);finally为确保一段代码无论发生什么异常情况都要被执行;t
- try语句能够嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到全部的try语句都完成。若是下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操做,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
这是三个不一样的概念,只是由于长得较像而被出成了一道题
final是一个修饰符,用来修饰类,变量,方法
- final修饰的类不能被继承
- final修饰的方法不能被重写
- final修饰的成员变量是不可变的,若是成员变量是基本数据类型,初始化以后成员变量的值不能被改变,若是成员变量是引用类型,那么它只能指向初始化时指向的那个对象,不能再指向别的对象,可是对象当中的内容是容许改变的
finally与try,catch一块儿搭配使用,不管是否catch到异常,finally中的内容都会执行
finalize是Object类中的方法,垃圾回收器在垃圾回收时会调用该方法,咱们能够在子类中重写该方法来作一些清理工做
在极特殊的状况下可能不执行
- 调用了System.exit()方法
- JVM崩溃了
- int是基本类型,Integer是int的包装类型
- 包装类型能够有一些本身的方法,引入包装类型可使java更好的面向对象
- 每一个基本类型都有其包装类:
- 原始类型: boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
扩展1:
java5中引入了自动拆装箱功能,例如在比较时能够自动拆装箱
class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
}
}
复制代码
扩展2:
一道和装箱有关的面试题
public class Test03 {
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2); //true
System.out.println(f3 == f4); //false
}
}
分析:自动装箱时,使用的时Integer的valueof方法,当int在-128到127之间时,并不会new一个新的对象,而是直接使用常量池中的Integer
具体分析:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
IntegerCache是Integer的内部类,其代码以下所示:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
简单的说,若是整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,
因此上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。
复制代码
答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会形成精度损失,所以须要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。
- 对于short s1 = 1; s1 = s1 + 1;因为1是int类型,所以s1+1运算结果也是int 型,须要强制转换类型才能赋值给short型。
- short s1 = 1; s1 += 1;能够正确编译,由于s1+= 1;至关于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
Java中 char型变量用来存储Unicode编码的字符,unicode编码字符集中包含了汉字,因此char类型能够储存汉字 char类型占两个字节
数组有length属性,String有length()方法
- 不是。
- java中8个基本类型为:byte、short、char、int、float、long、double、boolean。
- java中除基本类型外,都是引用类型(枚举是java5之后引入的特殊引用类型)
- String类型比较特殊,不可变。但它不是基本类型
String 类是final的,不能被继承
String 是只读字符串,StringBuilder和StringBuffer能够改变,StringBuilder效率高,线程不安全,StringBuffer线程安全。 在拼接String时,使用+编译器会帮咱们进行优化,使用StringBuilder进行拼接,这时+和StringBuilder没有多大区别。但当循环中使用+时,咱们应该显示的使用StringBuilder,以防止屡次调用new StringBuilder,形成没必要要的性能浪费。
循环中使用+举例:
String str = "hello,world!";
String result = "";
for (int i = 0; i < loopCount; i++) {
result += str;
}
这个时候编译器会优化成
String str = "hello,world!";
String result = "";
for (int i = 0; i < loopCount; i++) {
result = new StringBuilder(result).append(str).toString();
}
屡次new StringBuilder形成了性能浪费。
复制代码
扩展例题
class StringEqualTest {
public static void main(String[] args) {
String s1 = "Programming";
String s2 = new String("Programming");
String s3 = "Program";
String s4 = "ming";
String s5 = "Program" + "ming";
String s6 = s3 + s4;
System.out.println(s1 == s2); // false
System.out.println(s1 == s5); //true
System.out.println(s1 == s6); //false
System.out.println(s1 == s6.intern()); //true
System.out.println(s2 == s2.intern()); //false
}
}
解析:1. String是引用类型,这里 == 比较的是引用是否相同,便是否指向相同的地址
2. 在new String对象时,会产生一个新的对象,并不会使用常量池中的字符串
3. intern会在常量池中寻找该字符串(若是没有责新建),并返回他的地址
复制代码
两个对象,一个是静态区的"xyz";一个是用new建立在堆上的对象。
- String 转基本数据类型:调用基本数据类型对应包装类的parseXXX(String)或valueOf(String)方法
- 基本数据类型转String:基本数据类型+“”;String.valueof(12)
- 方法有不少,能够用StringBuffer/StringBuilder的reverse方法,这里reverse是经过位移实现的
- 再举例一种递归方法:
public String reverse(String originString){
if(originString == null || originString.length <= 1)
return originString;
return reverse(originString.subString(1)) + originString.charAt(0);
}
复制代码
- 线程安全,不可变天生线程安全
- String常被用做HashMap的key,若是可变会引有安全问题,如两个key相同
- String常被用做数据库或接口的参数,可变的话也会有安全问题
- 经过字符串池能够节省不少空间
- 每一个String对应一个hashcode,再次使用的话不用从新计算
计算机存储信息的最小单元是一个字节即8bit,因此能表示的范围是0~255,这个范围没法保存全部的字符,因此须要一个新的数据结构char来表示这些字符,从char到byte须要编码。
- ASCII:总共有 128 个,用一个字节的低 7 位表示,031 是控制字符如换行回车删除等;32126 是打印字符,能够经过键盘输入而且可以显示出来。
- GBK:码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字能够用 GBK 来解码,而且不会有乱码。
- UTF-16:UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符均可以用两个字节表示,两个字节是 16 个 bit,因此叫 UTF-16。UTF-16 表示字符很是方便,每两个字节表示一个字符,这个在字符串操做时就大大简化了操做,这也是 Java 以 UTF-16 做为内存的字符存储格式的一个很重要的缘由。
- UTF-8:统一采用两个字节表示一个字符,虽然在表示上很是简单方便,可是也有其缺点,有很大一部分字符用一个字节就能够表示的如今要两个字节表示,存储空间放大了一倍,在如今的网络带宽还很是有限的今天,这样会增大网络传输的流量,并且也不必。而 UTF-8 采用了一种变长技术,每一个编码区域有不一样的字码长度。不一样类型的字符能够是由 1~6 个字节组成。
Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程当中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程当中必须由用户指定 Charset 编码格式。
Unicode是字符集 UTF-8是一种编码方式,达到了对数据流压缩的目的
- 断言在软件开发中是一种经常使用的调试方式
- 断言检查一般在开发和测试时开启,发布时关闭
- 断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;若是表达式的值为false,那么系统会报告一个AssertionError。断言的使用以下面的代码所示:assert(a > 0); // throws an AssertionError if a <= 0
- Android中不推荐使用断言
Java中的引用有四种:强引用,弱引用,软引用,虚引用
- 强引用: 一般咱们使用new操做符建立一个对象时所返回的引用即为强引用
- 弱引用: 若一个对象只能经过弱引用到达,那么它就会被回收(即便内存充足),一样可用于图片缓存中,这时候只要Bitmap再也不使用就会被回收
- 软引用: 若一个对象只能经过软引用到达,那么这个对象在内存不足时会被回收,可用于图片缓存中,内存不足时系统会自动回收再也不使用的Bitmap
- 虚引用: 虚引用是Java中最“弱”的引用,经过它甚至没法获取被引用的对象,它存在的惟一做用就是当它指向的对象回收时,它自己会被加入到引用队列中,这样咱们能够知道它指向的对象什么时候被销毁
- 优势:动态代理与静态代理相比较,最大的好处是接口中声明的全部方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,咱们能够进行灵活处理,而不须要像静态代理那样每个方法进行中转。
- 缺点:它始终没法摆脱仅支持 interface 代理的桎梏,由于它的设计注定了这个遗憾。
- 并发:(在一个时间段内同时执行多件事)如单核cpu以时间片的方式让多个线程轮循执行,在外界看来他们是同时执行的
- 并行:(在一个时刻同时执行多件事)多核cpu每一个核运行一个App,他们是真正的同时执行
- 字节流:字节流继承于InputStream、OutputStream,以字节方式读取
- 字符流:字符流继承于Reader、Writer,以字符方式读取
- 字节流操做的基本单元是字节;字符流是Unicode字符
- 字节流不使用缓冲区,字符流使用缓冲区
- 字节流一般用于处理二进制数据,实际上它能够处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流一般处理文本数据,它支持写入及读取Unicode码元。
- IO面向流,NIO面向缓冲区
- IO是阻塞的,NIO是非阻塞的
- Java NIO的选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道
利用NewIO的FileChanel
- 私有化成员变量
- 经过构造方法初始化成员变量
- 只提供get方法,不提供set方法
- get方法返回的是一个拷贝副本
- java 中同步常指多线程时数据的同步,在多线程中若是数据不是线程安全的,那么有可能会出现不少错误
- 异步多指方法调用时,是否等待方法调用完成才继续向下执行,例如一般网络请求就是异步的。
- 序列化是将对象的状态信息转换为能够存储或传输的形式的过程。
- java将对象序列化成了字节信息
- java经过实现Serializable接口实现序列化
- for-each 使代码更加简洁优雅
- for-each 其实是经过迭代器(Iterator)实现的
支持Lambda 表达式,方法引用,增长了新的时间工具类等
XML的主要做用有两个方面:数据交换和信息配置。在作数据交换时,XML将数据用标签组装成起来,而后压缩打包加密后经过网络传送给接收者,接收解密与解压缩后再从XML文件中还原相关信息进行处理,XML曾经是异构系统间交换数据的事实标准,但此项功能几乎已经被JSON(JavaScript Object Notation)取而代之。固然,目前不少软件仍然使用XML来存储配置信息,咱们在不少项目中一般也会将做为配置信息的硬代码写在XML文件中,Java的不少框架也是这么作的,并且这些框架都选择了dom4j做为处理XML的工具,由于Sun公司的官方API实在不怎么好用
- DAO 是 Data Access Object 的缩写
- 位于业务逻辑和持久化数据之间
- 实现对持久化数据的访问。
所谓设计模式,就是一套被反复使用的代码设计经验的总结(情境中一个问题通过证明的一个解决方案)。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式令人们能够更加简单方便的复用成功的设计和体系结构。将已证明的技术表述成设计模式也会使新系统开发者更加容易理解其设计思路。 在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中给出了三类(建立型[对类的实例化过程的抽象化]、结构型[描述如何将类或对象结合在一块儿造成更大的结构]、行为型[对在不一样的对象之间划分责任和算法的抽象化])共23种设计模式,包括:Abstract Factory(抽象工厂模式),Builder(建造者模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式);Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解释器模式),Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(责任链模式)。 面试被问到关于设计模式的知识时,能够拣最经常使用的做答,例如:
- 他们都是接口
- Comparable是比较本实例和其余实例的大小
- Comparator是比较器,用来输入两个实例后比较两个实例大小
URI 是统一资源标识符,而URL 是统一资源定位符
- XML文档定义分为DTD和Schema两种形式,两者都是对XML语法的约束
- 其本质区别在于Schema自己也是一个XML文件,能够被XML解析器解析,并且能够为XML承载的数据定义类型,约束能力较之DTD更强大
- 对XML的解析主要有DOM(文档对象模型,Document Object Model)、SAX(Simple API for XML)和StAX(Java 6中引入的新的解析XML的方式,Streaming API for XML),其中DOM处理大型文件时其性能降低的很是厉害,这个问题是由DOM树结构占用的内存较多形成的,并且DOM解析方式必须在解析文件以前把整个文档装入内存,适合对XML的随机访问(典型的用空间换取时间的策略);SAX是事件驱动型的XML解析方式,它顺序读取XML文件,不须要一次所有装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户经过事件回调代码来处理XML文件,适合对XML的顺序访问;顾名思义,StAX把重点放在流上,实际上StAX与其余解析方式的本质区别就在于应用程序可以把XML做为一个事件流来处理。将XML做为一组事件来处理的想法并不新颖(SAX就是这样作的),但不一样之处在于StAX容许应用程序代码把这些事件逐个拉出来,而不用提供在解析器方便时从解析器中接收事件的处理程序。
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11. Math.round()获得的值大于等于原来的值。
2 << 3(左移n位至关于乘以2的n次方,右移n位至关于除以2的n次方)。
UML(Unified Modeling Language)是统一建模语言。为软件开发提供模型化和可视化支持,方便沟通交流。
UML定义了多种图形化的符号来描述软件系统部分或所有的静态结构和动态结构,包括:用例图(use case diagram)、类图(class diagram)、时序图(sequence diagram)、协做图(collaboration diagram)、状态图(statechart diagram)、活动图(activity diagram)、构件图(component diagram)、部署图(deployment diagram)等。在这些图形化符号中,有三种图最为重要,分别是:用例图(用来捕获需求,描述系统的功能,经过该图能够迅速的了解系统的功能模块及其关系)、类图(描述类以及类与类之间的关系,经过该图能够快速了解系统)、时序图(描述执行特定任务时对象之间的交互关系以及执行顺序,经过该图能够了解对象能接收的消息也就是说对象可以向外界提供的服务)。 用例图:
类图: ![]()
时序图: ![]()
![]()
import java.io.BufferedReader;
import java.io.FileReader;
public class MyUtil{
private MyUtil(){
throw new AssertError;
}
public static int countWordInFile(String filename,String word){
int counter = 0;
try(FileReader fr = new FileReader(filename){
try(BufferReader br = new BufferReader(fr)){
String line = null;
while((line = br.readLine()) != null){
int index = -1;
while(line.length() >= word.length() && (index = line.indexof(word) >= 0){
counter ++;
line = line.subString(index+word.length());
})
}
}catch(Exception e){
Log.e(e);
}
} catch(Exception e){
Log.e(e);
}
)
}
}
复制代码
import java.io.File;
class Test12 {
public static void main(String[] args) {
File f = new File("/Users/Hao/Downloads");
for(File temp : f.listFiles()) {
if(temp.isFile()) {
System.out.println(temp.getName());
}
}
}
}
若是须要对文件夹继续展开,代码以下所示:
import java.io.File;
class Test12 {
public static void main(String[] args) {
showDirectory(new File("/Users/Hao/Downloads"));
}
public static void showDirectory(File f) {
_walkDirectory(f, 0);
}
private static void _walkDirectory(File f, int level) {
if(f.isDirectory()) {
for(File temp : f.listFiles()) {
_walkDirectory(temp, level + 1);
}
}
else {
for(int i = 0; i < level - 1; i++) {
System.out.print("\t");
}
System.out.println(f.getName());
}
}
}
在Java 7中可使用NIO.2的API来作一样的事情,代码以下所示:
class ShowFileTest {
public static void main(String[] args) throws IOException {
Path initPath = Paths.get("/Users/Hao/Downloads");
Files.walkFileTree(initPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
System.out.println(file.getFileName().toString());
return FileVisitResult.CONTINUE;
}
});
}
}
复制代码
1. 获取当前年月日,时分秒
Calendar cal = Calendar.getInstance();
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH)); // 0 - 11
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
2. 取得从1970年1月1日0时0分0秒到如今的毫秒数
Calendar.getInstance().getTimeInMillis();
System.currentTimeMillis();
Clock.systemDefaultZone().millis(); // Java 8
3. 如何取得某月的最后一天
Calendar time = Calendar.getInstance();
time.getActualMaximum(Calendar.DAY_OF_MONTH);
4. 如何格式化日期
利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。Java 8中能够用java.time.format.DateTimeFormatter来格式化时间日期,代码以下所示
public static void main(String[] args) {
SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd");
Date date1 = new Date();
System.out.println(oldFormatter.format(date1));
// Java 8
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date2 = LocalDate.now();
System.out.println(date2.format(newFormatter));
}
复制代码
Calender calender = Calender.getInstance();
calender.add(Calender.DATE,-1);
calender.getTime();
在Java 8中,能够用下面的代码实现相同的功能。
import java.time.LocalDateTime;
class YesterdayCurrent {
public static void main(String[] args) {
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);
System.out.println(yesterday);
}
}
复制代码
public class Single{
private static Single instance = new Single();
private Single(){
}
public static Single getInstance(){
return instance;
}
}
复制代码
public <T extends Comparable<T>> void sort(T[] list){
boolean swap = true;
T temp;
for(int i = list.length - 1 ; i> 0 && swap; i--){
swap = false;
for (int j = 0;j<i-1;j++){
if (list[j].compareTo(list[j+1]) > 0) {
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
swap = true;
}
}
}
复制代码
}
public static <T> int binarySearch(T[] x, T key, Comparator<T> comp) {
int low = 0;
int high = x.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int cmp = comp.compare(x[mid], key);
if (cmp < 0) {
low= mid + 1;
}
else if (cmp > 0) {
high= mid - 1;
}
else {
return mid;
}
}
return -1;
}
复制代码