在循环结构中,当循环条件不满足或者循环次数达到要求时,循环会正常结束。但是,有时候可能需要在循环的过程中,当发生了某种条件之后 ,提前终止循环,这就需要用到下面几个关键词:
return; :直接使用return结束方法执行,用于没有返回值函数的方法② return value; :return 一个特定值,用于有返回值函数的方法。静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。
类名.方法名 的方式,也可以使用对象.方法名的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象 。不过,需要注意的是一般不建议使用对象.方法名的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。因此,一般建议使用类名.方法名的方式来调用静态方法。静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问。因此,在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。
子类对象.该方法调用的就是子类的声明的方法,此时我们称子类重写了父类的该方法。从Java5开始,Java支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数,比如method1方法,可以接受任意个数的方法。
public static void method1(String... args) {
//......
}
注意,可变参数只能作为函数的最后一个参数。
public static void method2(String arg1, String... args) {
//......
}
此外,遇到方法重载时,会优先匹配固定参参数的方法,因为固定参数的方法匹配度更高。实际上,Java的可变参数编译后会被转换成一个数组。

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。Byte、Short、Integer、Long 这4种包装类默认创建了数值[-128,127]的相应类型的缓存数据,Character创建了数值在[0,127] 范围的缓存数据,Boolean直接返回True or False。
//Integer源码
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static {
// high value may be configured by property
int h = 127;
}
}
//Character源码
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
private static class CharacterCache {
private CharacterCache(){}
static final Character cache[] = new Character[127 + 1];
static {
for (int i = 0; i < cache.length; i++)
cache[i] = new Character((char)i);
}
}
//Boolean源码
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
从上述代码不难看出,如果不超出对应范围就返回同一个对象,否则仍然会去创建新的对象。注意,两种浮点数类型的包装类Float、Double并没有实现缓存机制。有了上述知识,下面的结果也就不难理解了:
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true
Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false
Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false
此外,我们再看下一个问题:下面的代码结果是true还是false?
Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1==i2);
Integer i1=40这一行代码会发生装箱,也就是说这行代码等价于Integer i1=Integer.valueOf(40) 。因此,i1直接使用的是缓存中的对象。而Integer i2 = new Integer(40)会直接创建新的对象。因此,答案是false。
记住:所有整型包装类对象之间值的比较,全部使用equals方法比较。
装箱就是将基本类型用它们对应的引用类型包装起来,而拆箱就是将包装类型转换为基本数据类型。究其根本,装箱其实就是调用了包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。即Integer i = 10等价于Integer i = Integer.valueOf(10)而int n = i等价于int n = i.intValue();
浮点数精度丢失和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况,这也就是解释了为什么浮点数没有办法用二进制精确表示。BigDecimal可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过BigDecimal来做的。
基本数值类型都有一个表达范围,如果超过这个范围就会有数值溢出的风险。在Java中,64位long整型是最大的整数类型。我们可以使用BigInteger来存放超出范围的数据,BigInteger内部使用int[]数组来存储任意大小的整形数据。但相对于常规整数类型的运算来说,BigInteger运算的效率会相对较低。
两者的主要区别在于解决问题的方式不同:① 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。② 面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。另外,面向对象开发的程序一般更易维护、易复用、易扩展。

/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* naitive 方法,用于创建并返回当前对象的一份拷贝。
*/
protected native Object clone() throws CloneNotSupportedException
/**
* 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
*/
public String toString()
/**
* native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
*/
public final native void notify()
/**
* native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
*/
public final native void notifyAll()
/**
* native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
*/
public final native void wait(long timeout) throws InterruptedException
/**
* 多了 nanos 参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 毫秒。。
*/
public final void wait(long timeout, int nanos) throws InterruptedException
/**
* 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
*/
public final void wait() throws InterruptedException
/**
* 实例被垃圾回收器回收的时候触发的操作
*/
protected void finalize() throws Throwable { }
== 对于基本类型和引用类型的作用效果是不同的:① 对于基本数据类型来说,==比较的是值。② 对于引用数据类型来说,==比较的是对象的内存地址。从本质上来说,因为 Java 只有值传递,所以,对于==来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。equals()不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。Object类中equals()方法的源码如下:public boolean equals(Object obj) {
return (this == obj);
}
equals()方法存在两种使用情况:① 类没有重写equals()方法 :通过equals()比较该类的两个对象时,使用的默认是Object类equals()方法。等价于通过==比较这两个对象。② 类重写了equals()方法 :实际上,也都要求我们在创建类时,必须重写equals()方法让其实现比较两个对象中的属性是否相等;若它们的属性相等,则返回true(即认为这两个对象相等)。hashCode()的作用是获取哈希码(int整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在 JDK 的Object类中,这就意味着Java中的任何类都包含有hashCode()函数。另外需要注意的是:Object的hashCode()方法是本地方法,也就是用C语言或C++实现的,该方法通常用来将对象的内存地址转换为整数之后返回。public native int hashCode();
hashCode()方法和equals()方法都是用于比较两个对象是否相等。之所以在存在了equals()方法后还要设计hashCode()方法,是因为在一些容器(比如 HashMap、HashSet)中,有了hashCode()方法之后,判断元素是否在对应容器中的效率会更高。equals()方法也返回true,我们才认为这两个对象相等。③ 如果两个对象的hashCode值不相等,我们就可以直接认为这两个对象不相等。③ equals()方法判断两个对象是相等的,那这两个对象的hashCode值也要相等。equals()方法判断两个对象是相等的,那这两个对象的hashCode值也要相等,所以也就要求我们重写equals()时必须重写hashCode()方法。新版的String其实支持两个编码方案: Latin-1和UTF-16。如果字符串中包含的汉字没有超过Latin-1可表示范围内的字符,那就会使用Latin-1作为编码方案。Latin-1编码方案下,byte占一个字节(8位),char占用2个字节(16),byte相较char节省一半的内存空间。JDK官方就说了绝大部分字符串对象只包含Latin-1可表示的字符。如果字符串中包含的汉字超过Latin-1可表示范围内的字符,byte和char所占用的空间是一样的。

在Java中,所有的异常都有一个共同的祖先java.lang包中的Throwable类。Throwable类有两个重要的子类:
① Exception:程序本身可以处理的异常,可以通过catch来进行捕获。Exception 又可以分为Checked Exception(受检查异常,必须处理)和Unchecked Exception(不受检查异常,可以不处理)。
② Error:Error属于程序无法处理的错误 ,不建议通过catch捕获 。例如Java虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
不一定的,在某些情况下,finally中的代码不会被执行。比如finally之前虚拟机被终止运行的话,finally中的代码就不会被执行。
Java 泛型(Generics)是JDK5中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型。比如ArrayList这行代码就指明了该ArrayList对象只能传入Person对象,如果传入其他类型的对象就会报错。并且,原生List返回类型是Object,需要手动转换类型才能使用,使用泛型后编译器自动转换。
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
//如何实例化泛型类:
Generic<Integer> genericInteger = new Generic<Integer>(123456);
public interface Generator<T> {
public T method();
}
//实现泛型接口,不指定类型
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
//实现泛型接口,指定类型
class GeneratorImpl<T> implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
public static <E> void printArray(E[] inputArray) {
for (E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = {1, 2, 3};
String[] stringArray = {"Hello", "World"};
printArray(intArray);
printArray(stringArray);
。