java 控制台延缓输出_Java程序员面试基础知识 I

short、int和long的二进制值在内存中存储时采用二进制补码(Two’s Complement)。二进制补码的好处之一是只有一个零,即没有“负零”的概念,因而系统可以多存储一个负值。

BigInteger可以认为是没有上界的。

Java对象: 对状态和行为的封装,对象可以定义为一组变量的集合和一组方法的集合,这组变量可以看成是整合在一起表示一个复杂的实体,而这组方法提供了和这个实体相关的操作。对象都是引用类型,当创建的对象被赋给一个变量时,表示这个变量指向了那个内存位置。如果多个变量被赋予同一个对象(称为实例),那么这些变量指向的是同一个内存区域,通过一个变量对这个实例进行任何修改都会被其他变量看到。

final关键字的作用对于对象和对于基本类型是一样的。变量定义时设置变量的值,然后变量表示的内存位置存储的值不能变化。基本类型和对象的变量定义和内存位置都有很大的不同。尽管对象引用不能变化,但是这个对象中的值可以变化,除非这些值本身都是final的。

对象中的可见性修饰符(visibility modifier)控制的是对类中封装的状态以及控制实例行为的方法的访问权。4种类型的修饰符定义如下:

AAffA0nNPuCLAAAAAElFTkSuQmCC

static修饰符:类中定义的静态方法和变量属于类,但是不属于某个实例。静态方法和变量是所有实例共享的,通常通过类名来访问,而不是通过某个具体的实例来访问。

多态(polymorphism)和继承(inheritance)是面向对象开发中的两个核心概念。通过多态,可以为某一特定类型的行为进行定义,并且可以针对这种行为提供多种不同的实现类。通过继承,可以在定义一个类时从父类继承行为。

运行在JVM上的每一个类都是从java.lang.Object继承而来的。

equals(Object other) 方法的用途是测试两个引用表示的对象是否逻辑上相等。对于集合类,例如java.util.TreeMap和java.util.HashMap等,利用对象的equals方法判定对象是否已经在集合中存在。Object实现的equals方法比较的是对象在内存中的位置。

hashCode的规则是:对于两个相等的对象,必须返回同一个值。然而,如果对象返回的是同一个hashCode,不一定意味着两个对象相等。要注意的是,hashCode返回的是一个int,这意味着如果要求不同的hashCode表示不相等的对象,那么某一个类型的实例最多只能有232种不同的值。这是一个很大的限制,特别是对于String这样的对象。和equals一样,Object类的hashCode方法也是通过内存位置生成hashCode值。这意味着两个在不同位置但逻辑上相等的实例返回的hashCode会不同。

如果要重写hashCode或equals方法,那么这两个方法都必须重写。

hashCode和equals之间有这样的关系的原因和java.util.HashMap这一类集合类的实现方式有关。支撑HashMap的数据结构是某种类型的表,例如数组或列表,hashCode值的作用是判定表中保存对象要使用的索引值。由于hashCode返回的值是int,所以其值可能为负,也可能比表的大小要大。任何HashMap的实现都会对这个值进行操作,从而得到合法的表索引值。如果不相等的对象具有同样的hashCode值,那么这些对象都会通过LinkedList数据结构保存在同一个索引。

Java中的数组也是对象。因此数组是通过引用传递的。

String表示的值使用char数组来保存。String的值永远不会变化,String是不可变的。String的substring、replace和split方法返回的都是新的字符串拷贝,新字符串的内容做了相应的修改。所有的数值类,例如Integer、Double、Character和BigInteger也都是不可变的。

String驻留(interning):当JVM加载类时,会将所有的String字面量保存在一个常量池中,如果出现了重复的String字面量,那么重复字面量可以通过池中已经存在的相同常量来引用。任何String实例都可以通过intern()方法添加到这个驻留池中。如果要对来自一个文件或网络连接的大量数据进行解析,而且这些数据可能包含大量的重复内容,那么可以使用String驻留技术。但是如果对intern()方法的滥用导致常量池中存在很多条目,例如数百万条目,那么每一个条目的查询开销也会影响应用程序的运行性能。

String常量池是享元模式的一种实现。

结合泛型(generic)使用Collections API时,编译器就知道约束集合只允许包含特定类型的对象。

在应用泛型时存在类型变体(type variance),可通过通配符(wildcard)告诉编译器这里允许A的扩展类的任何实例,如:List extends A>。尽管B是A的子类,但是List并不是List的子类。

具体化(reified):本质上说,具体化的意思就是在运行时生效。Java的泛型类型是没有具体化的,即编译器检查实现代码使用泛型参数是否正确时使用的所有类型信息都不是.class文件中定义的类型信息。

自动装箱和装箱的区别:Java 5引入了自动装箱的概念,指的是自动地将基本类型转换为对应的引用类型,例如boolean转换为Boolean,以及int转换为Integer。在Java 5之前,这种操作必须手工完成,即装箱操作,为也将int转换为Integer,必须构造一个(new Integer(42)),或使用工厂模式(Integer.valueOf(42))。

拆箱: 将装箱的引用类型,例如Float、Integer和Boolean转换为对应的基本类型float、int和boolean的过程。必须注意引用为null的情况。当编译器将Integer转换为int时,会假定被转换的值不为null,如果被转换的值为null,则会立即抛出NullPointerException异常,因为无法将null赋值给基本类型。

基本类型不能用于泛型类型定义,即没有List等类型,泛型中必须使用引用类型。

注记(annotation):在Java 5中引入的,Junit库从Junit 4开始充分利用这些注记。方法可以有多个注记。

@Override注记:这条指令告诉编译器,有一个父类的方法要被重写。如果在父类中没有匹配的方法签名,那么表示出现了一个错误,编译应该停止。这是避免在重写方法时产生错误的绝好方法。

Java异常层次结构:

AAffA0nNPuCLAAAAAElFTkSuQmCC

如果一个方法(或构造函数)可能会抛出检查异常,那么在方法定义中应该显示地定义这个异常。这个方法的所有调用者都必须准备好处理这个异常,处理的方法既可以是将异常抛出给调用自己的方法,也可以是将方法调用包装在一个try/catch/finally代码块中,并且根据具体情况处理异常。Java标准库中的所有异常都可以在构造函数中接受一个throwable实例,如果要创建新的Exception类,请一定遵循这个规则。

运行时异常(runtime exception)和检查异常(checked exception):异常本身的定义分为两类:一个异常既可指运行时异常,也可指检查异常。运行时异常由RuntimeException的子类表示,检查异常则是任何其他的异常。作为一般性原则,RuntimeException异常是任何一位细心的开发者都应该避免的异常,如ArrayIndexOutOfBoundsException、NullPointerException。

异常类(exception chaining):当抛出一个之前已经捕捉到的异常里,建议抛出一个新的异常,并且在这个新的异常中添加一个引用,这种技术称为异常链。需要重新抛出异常的原因包括将一个检查异常转换为一个运行时异常,以及对异常执行一些日志操作然后再重新抛出这个异常。在对没有处理的异常进行调试时,栈跟踪记录可以在应用程序控制台输出所有这些信息,通过“caused by” 行可以找到被包装前或重新抛出前的原始异常。

try-with-resources语句:Java 7为try/catch/finally语句引入了一种语法糖,如果一个类实现了AutoCloseable接口,那么不需要担心资源关闭的问题。AutoCloseable接口指定了一个方法close(),这个方法会在try代码块之后调用,就好像在代码块的finally部分调用一样。

通过反射API(Reflection API)可以访问并修改所有字段,不论这些字段的可见性如何。通过反射机制对TestClass类的私有字段name进行修改:

public final class TestClass {

private String name;

public TestClass(String s){

name = s;

}

public String getName(){

return name;

}

public static void main(String[] args){

final TestClass testClass = new TestClass(“initName”);

// 使用反射API访问私有字段name

final Field name = testClass.getClass().getDeclaredField(“name”);

name.setAccessible(true);

name.set(testClass, “changedName”);

System.out.println(testClass.getName()); // 将会输出changedName

}

}

集合API类: 大致来说,集合框架包含三类:Set (HashSet、TreeSet、LinkedHashSet)、List (ArrayList、LinkedList、Vector)以及Map (HashMap、Hashtable、TreeMap)。还有一个专门的Queue接口 (PriorityQueue)。Java Collection框架包含在java.util包中,所有的单元素集合都实现了Collection接口,映射的实现没有实现Collection接口。Java倾向于将映射和集合分开,但二者还是有关联的:Map接口包含的entrySet()、keySet()和values()方法都以Collection的形式返回Map中的不同数据。

AAffA0nNPuCLAAAAAElFTkSuQmCC

LinkedHashMap具有HashMap的所有特性(根据key索引快速查找元素)但同时还能保留进入映射数据结构的条目的顺序。

Hashtable和HashMap:Hashtable类是synchronized,尽管对并行任务很有效,但对单线程任务会有显著的性能开销。而HashMap类不是synchronized,因此开发者可以自行将这个类的使用调整为满足任何特定的并发需求。建议在并行环境中需要严肃使用Map接口的地方使用ConcurrentHashMap类(Java 5中引入)。

Java虚拟机(Java Virtual Machine, JVM)是Java程序运行的平台。 Java程序被编译为字节码(bytecode)的形式(通过javac),JVM将字节码解释为适合底层架构和操作系统运行的具体指令。

内存的分配:内存主要被分为栈和堆。new关键字在Java堆(heap)中分配内存。堆是主要的内存池,整个应用程序都可以访问。如果无法为某个对象分配内存,则JVM会尝试通过垃圾回收机制回收一些内存。如果仍然无法获得足够的内存,则JVM抛出OutOfMemoryException异常并退出。

代(generation):堆被分为一些不同的区域,这些区域称为代(generation)。随着对象在越来越多次的垃圾回收中生存下来,它们会被提升至不同的代中。越老的代的垃圾回收频率越低。对象刚创建时被分配在Eden Space中,如果在一次垃圾回收中存活下来了,那么这些对象会被晋升到Survivor Space中。如果对象在Survivor Space中生存了更长的时间,那么这些对象会被分配到Tenured Generation中,这一代垃圾回收的频率要低得多。还有一个第4代,称为Permanent Generation,或简称为PermGen,这一代中的对象通常包含JVM运行时必不可少的不可变状态数据,例如类的定义和String常量池,这些对象是不会被垃圾回收的。

垃圾回收(garbage collection)指的是一种重新回收之前分配过的内存的机制,回收的内存可以重新给未来的内存分配使用。在Java中,当新构造一个对象时(通常是通过new关键字构造),JVM会为对象及其要保存的数据分配足够多的内存。当这个对象不再需要时,JVM需要重新回收这块内存。Java传统使用的垃圾回收算法是标记-清理算法。运行代码中的每一个对象引用都被标记为活跃,这些对象中的每一个引用都会被遍历,并且也被标记为活跃,直到从活跃对象出发的所有路径都被跟踪了。这个过程完成之后,堆中的每一个对象都会被扫描,那些没有被标记为活跃的内存位置都会被设置为可回收。在这个过程中,为了能够成功地回收内存,JVM中的所有线程都会被暂停,这种方式称为“停止一切(stop-the-world)”的回收。

栈中保存的内存包括原始值、指向对象的引用以及方法。栈上变量的生命期是由代码作用域决定的。一旦执行流程离开了作用域,那么这个作用域中声明的变量就都会从栈中移出。当调用一个方法时,这些声明的变量会被放置在栈的顶部,在一个栈中调用另一个方法时,新方法的变量会被压入栈中。

Java代码运行在JVM上的生命周期:编译器将Java代码编译输出字节码,并保存在.class文件中。字节码是针对JVM的指令,是一种二进制格式的代码,类似于具体平台和操作系统的可执行机器码指令。将类定义的字节码装入正在运行的JVM的内存的过程称为类加载(classloading)。JVM带有类加载器(classloader),负责解析.class文件并将文件加载到内存中。类加载器是一种抽象,能从磁盘、网络接口甚至诸如JAR这类存档文件中加载类文件。类只会在运行中的JVM需要某个类的定义时被加载。一旦一个类完成了加载,JVM自己就会对字节码进行验证以确保是合法的,包括验证字节码不会分支访问到其类字节外部的内存位置,还包括检查所有的代码指令都是完整的指令。一旦完成了代码验证,JVM就可以将字节码解释为底层平台和操作系统上对应的指令代码。

System.gc():System类中的静态方法gc的用途是告诉JVM运行一次垃圾回收,但是调用这个方法并不能保证一定会进行一次垃圾回收。

finalize():finalize方法是继承自Object的一个project方法。当JVM要对一个对象进行垃圾回收时,会首先调用这个对象的finalize方法。这个方法的作用是完成任何未了结的事情,以及关闭被垃圾回收的对象所依赖的所有资源。

java.lang.ref.WeakReference是一个泛型的容器类,当包含的实例没有强引用时,就可以被垃圾回收。

Java代码是运行在线程中的。创建一个新线程之前首先要构建一个新的Thread对象。Thread对象构建的时候接受Runnable接口的一个实例。Runnable接口只有一个方法:public void run()。注意在启动线程时,千万不要自己调用这个run方法,而是应该调用线程对象的start方法,当JVM创建好了运行代码的新线程之后会转而调用run方法。

Thread和Executor的区别。Executor接口是针对并行计算的一种抽象,Executor实例负责管理带缓存的线程池。Executor允许并发代码以一种受管的方式运行,接口中有一个方法:void execute(Runnable)。通过使用线程池,可以在需要线程的时候复用已经运行完其他代码的线程,而不是创建新的线程。Executors.newCachedThreadPool返回的不是一个Executor,而是一个ExecutorService。ExecutorService的主要优势在于并行计算可以返回一个结果,还支持关闭操作,而Executor使用的Runnable对象只有一个void返回类型。

public class UsingExecutor {

public static void main(String args[]) {

final Executor executor = Executors.newCachedThreadPool();

executor.execute(new ThreadPrinter());

executor.execute(new ThreadPrinter());

}

}

public class ThreadPrinter implements Runnable {

@Override

public void run() {

for(int i = 0; i < 5; i++) {

System.out.println("From new thread: " + Thread.currentThread().getName());

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

每一个Java对象都能给上锁。如果将代码块包装在synchronized(object)中,那么同一时刻只能有一个线程运行这个代码块。被synchronized代码块标记的对象被当成锁使用。

当x++和x—这样的语句在编译的时候,生成的字节码中包含了3个操作:将x中保存的值从内存中取出来,将这个值递增1,然后将x值写回内存。在这3个操作之间,JVM可能会中断操作,而切换至的线程可能会修改x值。如果发生这种情况,那么当JVM切换回原来的线程时,写回内存的值就是错误的,因此被中断的线程得到的就是错误的值。

使用不可变对象的一个主要优势在于:由于对象的值永远不可变化,因此可以随意在线程之间传递,而不需要使用锁。当需要更新某个不可变对象中的值时,通常都需要生成一份带有更新值的新拷贝。

根据类名创建一个实例:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值