SCJP的考试很简单,就是对细节语法考很深,所以我把一些网上的资料和自己遇到的问题进行了总结
希望能对想考SCJP的朋友有所帮助
1. 标识符可以以字母(a-zA-Z)、下划线(_)、$符开始
2. 一个源文件可以有多个非public类,但只能有一个public类,而且这个public类要和源文件的名字相同;如果没有public类,那么文件名没有限制
3. package语句必须是第一行非注释的语句
4. 类只可以是public或者default,default类只能由同一个包的其他类看到
5. 对于abstract类:
a) abstract类不能被实例化(不能被new出来)
b) 一个类不能既是abstract又是final
c) 一个abstract类可以既有abstract方法,又有普通方法
d) 继承abstract类的实体类必须实现所有abstract方法
e) abstract类的方法不可以是final或者private,因为它要被继承和实现
f) 一个声明为abstract的类不一定要有abstract方法,声明了abstract方法的类必须声明为abstract.
6. 对于interface:
a) 一个interface可以被类implement或者被另一个interface继承
b) interface可以看作一个100%的abstract类
c) interface不能含有实体方法,所有的方法都是abstract public的,无论生命的时候写不写这两个关键字
d) interface可以含有常量,所有的常量都是public static final的
e) 一个interface可以extends多个其他的interface
f) 当用一个abstract类来实现interface的时候,它不用override interface中的方法,也可以实现一部分,但是如果是实体类,就必须override所有的方法。实现的时候遵循override的所有准则(见override和overload)。
g) 注意在override的时候题目中经常会去掉public这样的修饰符,这样就不是正确的override了。
7. 对于类成员(方法和变量)
a) 一共有4个访问级别: public, protect, default, private
b) 如果一个类不能被访问,那么它的所有成员也不能被访问。所以要先看类的访问级别,再看成员的
c) 对于default和protect两个访问级别:
i. default成员只可以被同一个package中的其他类访问
ii. protect成员可被同一个包中的其他类+子类(不管来自本包还是其他包)访问
iii. protect = package + subclasses
8. 对于局部变量,只能用final修饰,没有访问修饰符,它们不会得到初始值,要手动初始化
9. strictfp适用于类和方法,native只适用于方法
10. 对于变长参数(var-args)
a) 从Java 5才开始有,接受0个到多个参数
b) 格式是type… name,注意…挨着type
c) 一个方法只可以有一个变长参数,而且要在最后面
11. 对于final成员变量:
a) final变量一旦被初始化,就不能再改变
b) final如果是引用类型,那么引用是不可更改的,但是它引用的对象都是可以更改的
c) final修饰的引用类型变量必须在构造函数结束前被初始化
12. 对于instanceof
a) 编译时刻就可以判断的,不是运行时刻
13. 对于autoboxing
a) 注意有这么个方法会出现在定义中:Integer Integer.getInteger(String)
14. this()和super()
a) 只能用在构造函数中
15. 关于数组:
a) 数组可以包含原始类型和对象,但是数组本身是一个object
b) 在声明数组的时候就指定其大小是不正确的,一定要new出来
c) 数组可以承载能够通过IS-A测试的对象
16. static方法和变量:
a) 它不属于任何类的对象,而属于整个类本身
b) 静态方法不能访问非静态成员变量或者方法
17. 关于enum枚举类型
a) enum的成员属于其定义的那种类型,不要把它认为是int或者t型
b) enum可以定义在类的外面,里面,但是不能在一个方法中
c) 如果enum定义在类的外面,就不能用static, final, abstract等修饰了
d) enum可以有构造函数,方法,变量和常量类体。但是它的构造函数永远不能直接在代码中调用,只有在其初始化的时候被自动调用
e) 注意enum声明最后的分号可有可无,都是对的
18. Overriding和Overloading
a) 第一要注意的是private的方法是hidden的,它不会被继承。题目中父类如果有与子类signature相同的类,如果父类中该方法private,不构成override
b) Override的时候boxing不适用
c) 在逻辑表达式中(<,>,==), 符号两边都是wrap类型也可以自动unbox比较大小
d) 要注意,override的时候,父类声明抛出的异常子类不必声明,或者声明更比父类更宽泛的异常类,或者多抛出一些unchecked异常,下面两个例子:
i. class B { public int m1() throws Exception{}}
class A extends B{public int m1(){}} `没问题!
ii. class B { public int m1()}
class A extends B{public int m1() throws Exception{}} 编译错误!!!
e)
异常
1. Exception分为checked和unchecked,RuntimeException以下的都属于unchecked,其他的属于checked。
2. 顾名思义,checked异常是虚拟机要检查的,所以或者throw它,或者catch它。对于unchecked异常,虚拟机不会管,但是你也可以声明抛出或者catch
3. 题目中会出现没有catch的情况,这个是合法的
4. Finally属于可有可无的。如果有finally,那么必定会执行。除非在catch中就System.exit()。
5. 如果有多个catch语句,则异常的顺序应该由特殊到一般,否则产生变异错误.
6. 如果在throw new Exception后再添加语句,会产生编译错误,因为后面的语句执行不到
7. Main()方法的声明中也可以加throws XXXException
断言
1. Assert关键字是在JDK1.4引入的。不能将assert既作关键字又做变量名。
2. 不要用assert来验证public方法的参数有效性,可以用其验证private方法的参数
3. 不要用assert来验证命令行参数的有效性
4. 不要会产生副作用的assert语句
5. 用assert来标注你认为永远不可能执行到的地方
6. 用-source 1.3来指定编译版本,用-da或者-ea来关闭和打开assert。可以指定某些类或者包被打开,如-ea:MyClass
基本赋值(Basic Assignment)
1. 文字整数默认为int型,整数表达式默认结果为int型
2. 浮点数默认为double型
3. 缩短一个初始类型会截断高位
垃圾回收
1. 当对象没有引用指向它时,它可以被VM垃圾回收
2. 即使对象有有效的引用指向它,如果没有live线程使用它,它仍然可能被VM回收
3. 在内部类中创建的对象可以被VM回收
String, StringBuffer/StringBuilder类
1. 对于String
a) 首先要记住的是String对象是不可改变的,但是指向String对象的引用可以改变。所以在使用String类中的方法时,要把返回值赋值给新的引用,否则原来的String对象将丢失。
b) String类是final的,不能够再继承。
c) 得到String对象的长度使用的是length()方法,String类并没有length这样的属性。
2. 对于StringBuilder和StringBuffer
a) StringBuffer是synchronized,因此比StringBuilder慢。
b) 这两个类的对象都是可以改变的,他们的方法会改变对象本身,无需再赋值。
3. 注意使用substring()的时候第二个参数是从1开始计算的。
4. 记住StringBuffer.delete(start, end)删除的是从索引start到end-1的字符串
内部类
1. 内部类不能有任何的static声明,所以访问内部类的唯一方式是在运行时刻有一个外部类的对象,然后才能创建内部类对象。同样也说明,你不可能用外部类的静态方法来创建内部类的对象。
2. 在碰到匿名内部类的题目时一定要注意大括号后面还要有一个分号。
文件 I/O
1. 需要记住的类是File, FileReader, BufferedReader, FileWriter, BufferedWriter, PrintWriter
2. 对于File类:
a) 它是文件和文件夹的一种抽象,并不代表文件系统中一个真正的文件。
b) 它不用于读写文件,而是用来创建、删除搜索文件或者文件夹。
c) 只有调用createNewFile()才会创建真实文件,用exists()来判定文件是否存在
d) File类的构造函数用String作为参数,没有直接用一个file对象创建另一个file对象的构造函数
3. FileWriter/BufferedWriter
a) FileWriter的构造函数由File或者String对象作为参数
b) BufferedWriter由Writer对象做参数创建
c) BufferedWriter比FileWriter方法更多更方便;常用BufferedWriter来wrap FileWriter
4. 通常创建的顺序是
File f = new File(“someFile”);
FileReader fr = new FileReader(f); 或者FileReader fr = new FileReader(“somefile”);
BufferedReader br = new BufferedReader(fr);
5. PrintWriter
a) 从Java 5开始,可以由File或者String对象创建,它还有个println()方法
b) 要注意PrintWriter自有的一些方法,如print(), format(), println()等等
Serialization
1. FileOuputStream/ObjectOutputStream, FileInputStream/ObjectInputStream
a) FileXXXStream是低层次的Stream,通常用ObjectXXXStream来wrap它们,这个类似于FileReader和BufferedReader的关系。
b) 声明为transient的变量和static变量不会被serialize。
c) de-serialize的时候构造函数不会调用。
d) 要serialize的类声明实现Serializable接口,这个是个空接口(marker interface)。
e) Has-a关系:
i. 在serialize的时候,虚拟机会自动做深度拷贝(注意拷贝的是对象内容而不是其引用)。如果类的定义中有引用类型的实例变量(instance variable),那么这个类也应该实现Serializable接口,除非这个引用没有指向new出的对象,否则会抛出NotSerializableException, 注意不是编译错误。例如:
class Leg {} class Dog implements Serializable { Leg leg;} 没问题
class Leg {} class Dog implements Serializable { Leg leg = new Leg();} 异常!
ii. 把类中的instance variable标为transient会防止该类被serialize, 但是这样会导致de-serialize的时候产生空指针,这个时候我们需要自己实现writeObject()和readObject()。注意这两个方法是private,这时候要用到defaultXXXXObject(),不要忘记用try/catch。
iii. 如果自己实现witeObject()和readObject(),其signature如下:
private void writeObject(ObjectOutputStream os) throws IOException
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException
f) 对于Is-a关系:
i. 如果父类实现了Serializable接口,子类自动实现。
ii. 如果子类实现了Serializable, 父类可以不实现。如父类没有实现Serializable,那么当de-serialize的时候,父类的默认构造函数会被调用(子类的构造函数不被调用)。而子类没有从父类继承的部分将初始化为默认值,子类构造函数不会被调用。
iii. 如果子类实现了Serializable而父类没有实现,父类必须要有默认构造函数,否则虽然不会产生编译错误,但是运行时会抛出异常InvalidClassException(参照例子TestSerialize.java)
Dates, Numbers & Currency
1. 两个实体类Date, Locale, 3个抽象类Calendar, DateFormat, NumberFormat
2. Date类的大部分方法都已经deprecated, 它由一个long来存储自1997年1月1日以来经过的秒数, Calendar有比它更丰富的计算日期的方法,但是其他类DateFormat, NumberFormat都要用它来存储日期。
3. 关于DateFormat和NumberFormat:
a) 它是一个抽象类,所以只能有静态工厂方法获得(具体见构造函数表)
b) 其构造函数最多无非两个参数style,locale,可见其需要一个Locale的对象来构造,一旦对象创建,其locale不可改变
c) DateFormat有一个非静态getNumberFormat()方法返回一个NumberFormat类型
d) DateFormat和NumberFormat都有一个format(Date)方法来输出字符串
4. Locale构造函数有两个参数,一个language, 一个country
5. 主要是考这几个类之间的关系,构造函数(是否工厂方法)和常用方法
Parsing, Tokenizing, and Formatting
1. 关于正则表达式(regular expressions/ regex):
a) 正则表达式用于创建一个模式(pattern)或者元字符(metacharacters), 以此来匹配要搜索的字符串等内容
b) 正则表达式中:
i. "d 表示数字
ii. "s 表示空格符
iii. "w 表示一个字符(字母,下划线,数字)
iv. . 表示任何字符
c) 量化符(quantifiers)和贪婪匹配
i. ? 表示0次或1次
ii. * 表示0次或多次
iii. + 表示一次或多次
iv. 默认是贪婪匹配,对应的非贪婪匹配模式符号为??, *?, +?
d) 使用java.util.regex.Pattern和Matcher来模式匹配
i. Pattern p = Pattern.compile(args[0]);
Matcher m = p.matcher(args[1]);
while(boolean b = m.find()) {
System.out.println(m.start() + " " + m.group())}
ii. 注意这么几个方法compile(regex), matcher(source)。
iii. Matcher类的几个方法,find()从source开始找到下一个匹配是否存在,start()返回上一个匹配的索引,group()返回上一个匹配的字符串
e) 关于replace的部分不要求掌握
2. 对于tokenizing
a) 用分隔符delimiter将字符串分开的过程叫做tokenizing,分号的字符串叫做tokens
b) 有两种方式来tokenizing, 一种是String.split, 它的signature是:
public String[] split(String regex)
c) 第二种方式是用Scanner类
i. 默认的delimiter是空格,可以用useDelimiter()来设定delimitor
ii. 它用循环来一个个得出下一个token,所以可以随时退出。hasNextXxx()测试下一个token的值但并不返回下一个token。
iii. nextXxx()返回得到下一个token的值,并且移动到下一个token
iv. 注意如果没有匹配那么next()返回的是整个source
3. 对于格式化输出:
a) 有两个方法要考,format()和printf(),它们两个功能相同,是PrintStream类的方法。System.out是它的一个对象
b) 对于其中的format string
i. 它的具体格式是%[arg_index$][flags][width][.precision]conversion char
ii. arg_index$表示参数,如2$为第二个参数
iii. 注意width表示最小输出的字符数
iv. Flags: “–“(左对齐). “+”(加上正负号), “0”(填充0), “,“(使用数字分隔符如123,456), “(“ (括号将负数包含起来)
v. Conventions: b(boolean), c(char), d(integer), f(float), s(string)
vi. 如果参数类型不匹配,会抛出异常IllegalFormatConversionException
4. 考试中要注意两点,一类型是否匹配, 二width表示的是最小输出字符数
Collections
1. == v.s. equals(): ==判断的是两个引用是不是指向同一个对象,而equals()判断两个对象是否真正意义上相等
2. equals()和hashCode():
a) 如果两个对象equals(),那么他们的hashCode()也相等
b) 但是如果hashCode()相等,它们不一定equals()
3. HashMap, HashSet, Hashtable, LinkedHashMap和LinkedHashSet都用hash
4. 注意区别题目中legal和appropiate,返回常数的hashCode()是legal的,但是效率不高
5. Transient变量不适合用在equals()和hashCode()中
6. 有三个sorted类:TreeSet, TreeMap和PriorityQueue
7. 各个容器类的特点:
a) ArrayList迭代速度快,随机访问速度快,允许null
b) LinkedList添加删除速度快, 适合在尾部添加元素
c) HashSet快速访问,没有重复,但是无法迭代
d) LinkedHashSet按插入顺序迭代
e) HashMap允许一个null key, 多个null value
f) Hashtable是HashMap的对应类,不允许null, 线程安全
g) LinkedHashMap快速迭代,也允许一个null key和多个null value,按照插入顺序或者最后访问顺序迭代
h) PriorityQueue按照元素的优先级排序, sorted
8. 一些容器类的特有方法:
a) Iterator: 很多List和Set都可以迭代,传统的方法是用Iterator: Iterator it = list.iterator(); hasNext()方法返回是否还有下一个,并不向后移动iterator。 next()返回下一个元素并且向后移动iterator。
b) Map的key必须实现equals()和hashCode(), HashMap往Map中put()时候,会先判断hasCode()是否相等,相等的话再用equals()判断是否重复,如果重复就刷掉前面的值。所以如果类的实现没有override hashCode(),那么即使出现重复也可以放进去。
c) Queue接口下面有两个类,一个是LinkedList, 另外一个是PriorityQueue, 它们都有一些特有的方法,offer(), peek(), poll()。offer()向容器中添加元素,peek()返回队列头部,但是不从队列中删除;poll()返回并删除。
9. Arrays和Collections:
a) 用它查找时binarySearch()之前一定要先sort()过,而且要用同一个Comparator
b) 用Arrays.asList()从一个数组创建一个List对象,注意这个List和数组是连在一起的,共同变化
c) Collections.reverse()把List中的元素反过来,reverseOrder()返回一个和原来Comparator相反的Comparator
d) List接口和Set接口都有一个toArray()方法转化成数组
i. Object[] toArray()
ii. <T> T[] toArray(T[] a)
10. 同步与不同步(synchronized)
a) 有几个类是同步的: Hashtable, Vector, 非容器类有StringBuffer
b) 于此对因不同步的:HashMap, ArrayList, 非容器类的有StringBuilder
11. 关于Comparable和Comparator
a) 如果要用Comparable,该类就要implements Comparable, 实现int compareTo(Object)方法,然后直接用Collections.sort(collectionToSort)就可以了, Arrays相同
b) 如果要用Comparator, 就要实现compare(Object, Object)方法。然后用Colletions.sort(collectionToSort, comparator);
12. 下面的所有容器类要掌握
泛型
1. Generics是编译时刻的类型安全,运行时刻对于虚拟机来说都一样
2. 当把一个使用泛型的collection放进一个一个非泛型的方法中时,编译器就无法阻止该方法往里面放不兼容类型的对象。编译会通过,但会产生一个编译器警告,告诉你这样做可能有危险。
3. 要注意题目中compile without error不等于compile without warning。
4. 多态赋值只适用于base type,而不适用于泛型类参数,如:
a) List<Animal> aList = new ArrayList<Animal>(); 可以
b) List<Animal> aList = new ArrayList<Dog>(); 不可以
5. 可以在参数中使用wildcard, 如:
a) void addD(List<? extends Dog>){} 可以传入Dog及其子类对象
b) 注意extends关键字这里既适用于类也适用于接口
c) 使用了extends的容器只能被访问不能被修改
6. 可以用来声明自己的泛型类和方法,两个例子如下:
a) class Sample<T>{}
b) public <T> void samleMethod(T t){} 注意这里T不是返回值
7. 注意? 是用在参数中的,而T是声明泛型方法用的,不要混淆:
a) public void addD(List<? extends Dog>){}
b) public <T extends Animal> void addD(T t){}
线程
1. 要注意题目是否正确实现了run()方法,经常会缺少public关键字作为陷阱。正确run()方法的signature是:public void run()
2. 注意构造新的thread对象时,总是要用new Thread。如果是myRunnable实现了Runnable,那么用new Thread(myRunnable)
3. Thread类对象的start()方法只能调用一次,否则会抛出java.lang.IllegalThreadException,值得注意的是一个线程抛出了异常,其他线程仍然会执行。
4. sleep()方法的signature是:
public static void sleep(long millis) throws InterruptedException
要注意以下几点:
a) 它是静态(static)方法,会使当前正在执行的线程休眠最少millis毫秒
b) 要用try/catch或者声明throws InterruptedException
c) 它不会丢失monitor或者说lock
5. 关于线程的优先级要注意:
a) Thread定义优先级常量MIN_PRIORITY(1), NORM_PRIORITY(5)和MAX_PRIORITY(10)
b) 如果线程没有用setPriority()设置优先级,新线程将与创建者优先级相同。
c) 可以确定的是当前正在运行的线程不小于线程池(thread pool)中线程的优先级。
6. yield()方法的signature是:
public static void yield() throws InterruptedException
a) 它是静态(static)方法,将当前正在运行的线程变为runnable状态
b) 没有保证说它不会从线程池中被再次选中变为running状态
c) 要用try/catch或者声明throws InterruptedException
7. join()方法的signature是:
public final void yield() throws InterruptedException
a) 调用a.join()会使当前正在运行的线程等待,先执行完a,然后继续执行。
b) 要用try/catch或者声明throws InterruptedException
c) 它有两个重载:join(long millis)和join(long millis, int nanos)
8. 关于synchronized修饰符
a) synchronized可以用来修饰方法和语句块,用于语句块的时候要会lock某个对象
b) 不能用synchronized来lock一个primitive类型的变量(int/long/boolean等)
c) 每个Object都有一个lock,非静态方法可以lock对象
d) 每个类都有一个java.land.Class(比如MyClass.class),静态方法可以用它来lock
9. 关于wait()/notify()/notifyAll()
a) 它们都是Object类的方法,不属于Thread类
b) 调用wait()将立即释放对象的lock,而notify()并不会立即释放lock,要等synchronized语句块结束才会释放。
c) 三个方法都只能在synchronized的上下文中使用,而且要有当前的object才行
d) notify()无法指定唤醒某个线程,这个是由虚拟机决定的