四种访问级别
Java 中有三个很常用的关键字:public protected 和 private。我们可以称呼他们为访问控制符,也可称呼为作用域。怎么称呼都不重要,重要的是理解他们的作用及用法。
Java访问级别包含两个部分:
对类和访问级别。在对类的访问进行限制的时候,关键字可以是public或者不明确指定类修饰符(package-private)。
对成员的访问级别。在对类里面的成员做访问限制时,可以使用public,protected,package-private(不指明关键字),private。
下面的表格总结了不同的关键字在修饰成员时候的访问级别,访问级别决定了类中的字段和方法的可访问性。
异常分类
所有异常都是继承 Throwable 而来,写 Java 程序时需要注意 Exception 分支,但是 RuntimeException 是由于程序写错导致的,捕获时应考虑除了这两种的其他异常。
其中 Error 类、RuntimeException 及其子类称之为未检查异常,剩下的为已检查异常。
Error 类描述 Java 运行时系统的内部错误和资源耗尽错误。
RuntimeException 包括错误的类型转换、数组访问越界、访问空指针等等。
泛型中相关符号含义
E – Element (在集合中使用,因为集合中存放的是元素)
T – Type(Java 类)
K – Key(键)
V – Value(值)
N – Number(数值类型)
? – 表示不确定的java类型(无限制通配符类型)
S、U、V – 2nd、3rd、4th types
Object – 是所有类的根类,任何类的对象都可以设置给该 Object 引用变量,使用的时候可能需要类型强制转换,但是用使用了泛型 T、E 等这些标识符后,在实际用之前类型就已经确定了,不需要再进行类型强制转换。
length 与 length()
Java 中数组有 length 属性,而字符串有 length() 方法。这是为什么?
为什么数组有 length 属性?
首先,数组是一个容器对象(Java中的数组是对象吗?),其中包含固定数量的同一类型的值。一旦数组被创建,他的长度就是固定的了。数组的长度可以作为final
实例变量的长度。因此,长度可以被视为一个数组的属性。
有两种创建数组的方法,但是无论使用哪种方式,一旦数组被创建,其大小就固定了。
通过数组表达式创建数组。
通过初始化值创建数组。
使用表达式创建数组方式如下,该方式指明了元素类型、数组的维度、以及至少一个维度的数组的长度。该声明方式是符合要求的,因为他指定了一个维度的长度(该数组的类型为int,维度为2,第一维度的长度为3)。如
int[][] arr = new int[3][];
使用数组初始化的方式创建数组时需要提供所有的初始值。形式是使用{
和}
将所有初始值括在一起并用,
隔开。
注:
这里可能会有一个疑问,既然数组大小是初始化时就规定好的,那么int[][] arr = new int[3][];
定义的数组并没有给出数组的第二维的大小,那么这个 arr 的长度到底是如何“规定好”的呢?
其实,arr 的长度就是 3。其实 Java 中所有的数组,无论几维,其实都是一维数组。例如 arr,分配了 3 个空间,每个空间存放一个一维数组的地址,这样就成了“二维”数组。但是对于 arr 来说,他的长度就是 3。
int[][] a = new int[3][];
System.out.println(a.length());
int[][] b = new int[3][5];
System.out.println(b.length);
那为什么没有定义一个类似 String 一样的 Array 类?这是因为数组也是对象,所以这样写也合法。
Object obj = new int[10];
数组包含所有从 Object 继承下来方法(Java中数组的继承关系),除 clone() 之外。为什么没有一个 Array 类呢?在 Java 中没有Array.java
文件。一个简单的解释是它被隐藏起来了(注:Java 中的数组有点类似于基本数据类型,是一个内建类型,并没有实际的类与他对应)。你可以思考这样一个问题——如果有一个 Array 类,那它会像什么样?它会仍然需要一个数组来存放所有的数组元素,对吗?因此,定义出一个 Array 类不是一个好的主意。
为什么字符串有 length() 方法?
String 背后的数据结构是一个 char 数组,所以没有必要来定义一个不必要的属性(因为该属性在 char 数值中已经提供了)。和 C 不同的是,Java 中 char 的数组并不等于字符串,虽然 String 的内部机制是 char 数组实现的。(注:C 语言中,并没有 String 类,定义字符串通常使用char string[6] = "hollis";
的形式)
注:要想把 char[] 转成字符串有以下方式:
char [] s = {'a','b','c'};
String string1 = s.toString();
String string2 = new String(s);
String string 3 = String.valueOf(s);
可变参数
什么是可变参数
可变参数 (variable arguments) 是在 Java 1.5 中引入的一个特性。它允许一个方法把任意数量的值作为参数。
public static void main(Stirng[] args)
{
print("a");
print("a","b");
print("a","b","c");
}
public static void print(String ... s)
{
for(String str : s) System.out.println(a);
}
可变参数的工作原理
可变参数在被使用的时候,他首先会创建一个数组,数组的长度就是调用该方法是传递的实参的个数,然后再把参数值全部放到这个数组当中,然后再把这个数组作为参数传递到被调用的方法中。
可变参数的使用场景
通过其定义我们知道,当一个方法可能要接收任意数量的参数的时候使用可变参数是非常有用的。Java SDK 中有个很好的例子:String.format(String format, Object… args)。一个字符串可以中可以包含任意个待格式化参数,所以使用可变参数。
String.format("An integer: %d", i);
String.format("An integer: %d and a string: %s", i, s);
组合和继承
继承(Inheritance)
是一种联结类与类的层次模型。指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;继承是一种is-a
关系。
组合 (Composition)
体现的是整体与部分、拥有的关系,即has-a
的关系。
区别与联系
在继承结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种白盒式代码复用。(如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;)继承,在写代码的时候就要指名具体继承哪个类,所以,在编译期就确定了关系。(从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。)
组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说这种方式的代码复用是黑盒式代码复用。(因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法;)组合,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定。
建议在同样可行的情况下,优先使用组合而不是继承。因为组合更安全,更简单,更灵活,更高效。