一、数据类型
包装类型
八个基本类型:
- boolean/1
- byte/8
- char/16
- short/16
- int/32
- float/32
- long/64
- double/64
注:String不是基本类型。每个基本类型都有其对应的包装类型,基本类型与包装类型之间的转换称之为装箱与拆箱,但这种操作一般是是自动完成的。
Integer x = 1; //装箱
int y = x; //拆箱
缓存池
new Integer(123)与Integer.valueOf(123)是不同的,前者会创建一个Integer对象,而后者会使用缓存对象,也就是多次使用Integer.valueOf(123)获得的Integer对象很可能是同一对象。举例如下:
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
注:在自动装箱的过程中编译器其实就是调用了valueOf()方法,因此多个Integer实例使用自动装箱来创建并且值相同,那么就会引用相同的对象。
二、String
概述
首先看一下String源码的开头部分
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
String类被声明为final,说明不可被继承。
内部使用char数组存储数据,数组也被声明为final,说明数组初始化后也就不能被引用其他数组了,而且查看API文档可知,String类内部方法中也没有改变value数组的方法,因此String具有不可变性。
不可变的好处
1.可以缓存hash值
String的hash值经常被使用,例如String用作HashMap的key。不可变性使得hash值不会发生改变,所以只需要计算一次即可。
2.String Pool的需要
如果一个String已经被创建过,就会被存储到字符串常量池中,将来就会从String Pool中应用。只有 String 是不可变的,才可能使用 String Pool。
3.安全性
String常作为参数,String的不可变性保证的参数不会被改变。比如:在如下这种情况下结果输出仍然为"1"。
public static void main(String[] args){
String s = "1";
changeStr(s);
System.out.println(s);
}
public static void changeStr(String s){
s = "2";
}
4.线程安全
String的不可变性天生具备线程安全,可以在多个线程中安全使用。
String,StringBuffer,StringBuilder
1.可变性
- String不可变
- StringBuffer和StringBuilder可变
2.线程安全
- String不可变,因此线程安全
- StringBuilder不是线程安全的,但开销比StringBuffer小
- StringBuffer是线程安全的,内部使用synchronized来同步
String.intern()
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
String s1 = new String("1");
String s2 = "1";
System.out.println(s1==s2); //false
System.out.println(s1==s1.intern()); //true
上面有两种创建String对象的方法,第二种形式创建的字符串实例会自动将对象放入字符串常量池(String Pool)中。所以二者对象地址不同,s1==s2就为false。
三、继承
访问权限
子类继承父类,表示子类会继承父类的一切,即父类的所有成员变量及方法。但是,我们会发现,有部分父类的方法和变量我们是无法进行访问或调用的,这就与它们的访问权限相关了。
- private修饰词,表示成员是私有的,只有自身可以访问;
- protected,表示受保护权限,体现在继承,即子类可以访问父类受保护成员,同时相同包内的其他类也可以访问protected成员。
- 无修饰词(默认),表示包访问权限(friendly, java语言中是没有friendly这个修饰符的,这样称呼应该是来源于c++),同一个包内可以访问,访问权限是包级访问权限;
- public修饰词,表示成员是公开的,所有其他类都可以访问;
注:子类重写继承的方法时,不可以降低方法的访问权限,子类继承父类的访问修饰符要比父类的更大,也就是权限更加开放。
抽象类与接口
1.抽象类
抽象类和抽象方法都使用 abstract 进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
public abstract class AbstractClassExample {
protected int x;
private int y;
public abstract void func1();
public void func2() {
System.out.println("func2");
}
}
public class AbstractExtendClassExample extends AbstractClassExample{
@Override
public void func1() {
System.out.println("func1");
}
}
// AbstractClassExample ac1 = new AbstractClassExample();// 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
ac2.func1();
2.接口
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。
接口的成员(字段+方法)默认都是public的,并且不允许定义为 private 或者 protected。
接口的字段默认都是 static 和 final 的。
public interface InterfaceExample {
void func1();
default void func2(){
System.out.println("func2");
}
int x = 123;
// int y; // Variable 'y' might not have been initialized
public int z = 0; // Modifier 'public' is redundant for interface fields
// private int k = 0; // Modifier 'private' not allowed here
// protected int l = 0; // Modifier 'protected' not allowed here
// private void fun3(); // Modifier 'private' not allowed here
}
/*************************************************************************/
public class InterfaceImplementExample implements InterfaceExample {
@Override
public void func1() {
System.out.println("func1");
}
}
/************************************************************************/
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);
3.比较
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。 | 如果你往接口中添加方法,那么你必须改变实现该接口的类。 |
4.使用选择
使用抽象类:
- 需要在几个相关的类中共享代码,例如一些方法的默认实现。
- 需要能控制继承来的成员的访问权限,而不是都为 public。
- 需要继承非静态(non-static)和非常量(non-final)字段。
使用接口:
- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
- 需要使用多重继承。
在很多情况下,接口优先于抽象类,因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。
super
- 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而完成一些初始化的工作。
- 访问父类的成员:如果子类覆盖了父类的中某个方法的实现,可以通过使用 super 关键字来引用父类的方法实现。
重写与重载
- 重写(Override)
存在于继承体系中,是指子类实现了一个与父类在方法声明上完全相同方法,也可以理解为外壳不变,核心重写!
注:
1.重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。
2.访问权限不能比父类中被重写的方法的访问权限更低。
3.声明为final的方法不能被重写。声明为static的方法不能被重写,但是能够被再次声明。 - 重载(Overload)
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。应该注意的是,返回值不同,其它都相同不算是重载。