1.
class Parent
{
public String tag = "疯狂java讲义";
}
class Derived extends Parent
{
private String tag = "tag会覆盖掉父类中的tag";
}
public class HideTest
{
public static void main(String[] args){
Derived d = new Derived();
//不能访问私有变量
//System.out.println(d.tag);
//向上转型后可以访问实例变量
System.out.println(((Parent)d).tag);
}
}
2.子类构造器执行体中既没有super调用,也没有this调用,系统会在执行子类构造器前隐式调用父类无参数的构造器。
创建任何Java对象,最先执行的总是java.lang.Object类的构造器。
super可以用来访问被子类覆盖的父类的方法或变量。this代表对象本身,可以用来区分成员变量和局部变量。
class Parent
{
public int field=1;
// 注释掉后会出现找不到fun()的编译错误
/*
public void fun(){
System.out.println("Parent");
}
*/
}
class Dervid extends Parent
{
public int field=2;
public void fun(){
System.out.println("Dervid");
}
}
public class Test
{
public static void main(String[] args){
Parent p = new Dervid();
System.out.println(p.field);//运行输出 1
p.fun();
}
}
4.
class Base
{
public Base(){
test();
}
public void test(){
System.out.println("将被子类重写的方法");
}
}
public class Sub extends Base
{
private String name;
public void test(){
System.out.println("子类重写父类的方法"+name.length());
}
public static void main(String[] args){
//下面代码引发空指针异常,因为从父类调用name时,还没有生成子类实例
Sub s = new Sub();
}
}
5.缓存是一种非常优秀的设计模式,在Java、JavaEE平台的很多地方都会通过缓存来提高系统的运行性能。简单的说,如果你需要一台电脑,那么你就去买了一台电脑,但是你不可能一直使用这台电脑,你总会离开这台电脑--在你离开电脑的这段时间内,你不会把电脑扔掉,而是把它放在房间里,下次用的时候直接开机使用,而不是重新购买一台。由于房间空间有限,有些东西用过一次就扔掉了,一般只把购买成本大,频繁使用的东西缓存起来。
public class Test
{
public static void main(String[] args){
Integer ina = 2;
Integer inb = 2;
System.out.println("两个2自动装箱是否相等:"+(ina==inb));
Integer biga = 128;
Integer bigb = 128;
System.out.println("两个128自动装箱是否相等:"+(biga==bigb));
}
}
Integer类对经常使用的-128-127的整数进行了缓存。源码如下:
static final Integer[] cache = new Integer[-(-128)+127+1];
static {
for(int i=0;i
7.对于instanceof而言,如果前面对象是后面类或其子类的实例时,都返回True,所以重写equal是方法判断两个对象是否为同一个类的实例是有问题的。改为t.getClass()==Person.class。这行代码用到了反射基础。
8.
public class NullAccessStatic
{
private static void test(){
System.out.println("static修饰的类方法");
}
public static void main(String[] args){
NullAccessStatic nas = null;
nas.test();
}
}
null对象可以访问它所属类的类成员。类Field:必须在静态块中或声明该Field时指定其初值。
实例Field:必须在非静态初始化块,声明该Field或构造器中指定初始值。
10.
String s1="疯狂Java";
String s2="疯狂"+"Java";
System.out.println(s1==s2);
String str1="疯狂";
String str2="Java";
String str3=str1+str2;
System.out.println(s1==s3);
s1==s2输出true,s1==s3输出false,因为str2与str3只是普通变量,编译器不会执行宏替换,因此编译器也就无法在编译时确定str3的值,自然也就无法让其指向常量池中的s1.
11.final修饰的方法仅仅是不能被重写,而不是不能覆盖。
class Base
{
public static final void fun(){
System.out.println("final 修饰的方法");
}
//正确
public static final void fun(int a){
System.out.println("重载final修饰的方法");
}
}
class Derived extends Base
{
//错误,不允许覆盖final方法
/*
public static void fun(){
System.out.println("覆盖final修饰的方法");
}*/
}
BufferedReader是Java IO流的一个字符包装流,他必须建立在另一个字符流的基础上。但标准输入System.in是字节流,程序需要使用转换流InputStreamReader将其包装成字符流。所以程序中用于获取键盘输入的BufferedReader对象采用如下代码创建。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
字符流与字节流的区别:
字节流是最基本的,所有的InputStream和OutputStream的子类都是,主要用在处理二进制数据,它是按字节来处理的。
但实际中很多数据是文本,于是提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化。
这两个之间通过InputStreamReader和OutputStreamWriter来关联,实际上是通过byte[]和String类来关联。
13.Object类提供的clone()函数方法不仅能简单的处理复制对象的问题,而且这种自我克隆机制十分高效。比如克隆一个包含100个元素的int[]数组,用系统默认的clone方法比静态copy方法快接近2倍。需要指出的是,Object类的clone()方法虽然简单,但是它只是一种浅克隆。
14.创建BigDecimal对象时,不要直接使用double作为参数来调用构造器,否则会发生精度丢失的问题。
15.
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
class Test
{
public static void main(String[] args){
Collection books = new HashSet();
books.add("疯狂Java讲义");
books.add("轻量级JavaEE企业应用实践");
books.add("疯狂Android讲义");
Iterator it = books.iterator();
while(it.hasNext()){
String book = (String)it.next();
System.out.println(book);
if(book.equals("疯狂Android讲义")){
//编译通过,运行时发生ConcurrentModificationException
//迭代过程中,不能修改集合元素
books.remove(book);
}
}
}
}
16.简单的说,HashSet判断元素相等是通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。当把一个对象放进HashSet中时,如果需要该对象对应类的equals方法,则也应该重写它的HashCode方法。规则是:当两个对象通过equals返回true时,其hashCode值也应该相同,否则两个相同的对象都可以加入HashSet,存储于不同的位置。
当向HashSet中添加可变对象时必须小心,防止造成不可准确访问的问题。
17.当一个对象添加到TreeSet时必须实现Comparable接口。当HashSet中对象数大于1个时,会调用对象的compareTo(Object obj)方法。
18.IdentityHashMap与HashMap的实现机制基本相同。不同的是IdentityHashMap只有在两个key严格相等时,才会认为两个key相等。对于普通HashMap,只要通过equals方法比较相等,并且hashCode值相等即可。
19.当创建了带泛型声明的父类、接口之后,可以为该接口创建实现类或从父类派生子类,但需要指出的是,当使用这些接口、父类时不能再包含类型形参。
//Apple类不能跟形参
public class A extends Apple
如果想从Apple类派生一个子类,可以采用如下代码:class A extends Apple
调用方法时必须为类型形参传入参数值,与调用方法不同的是,使用类、接口时可以不为类型形参传入具体的值,下面也是正确的:
class A extends Apple{}
如果从Apple
例如:ArrayList
下面的代码输出true。
List
List
System.out.println(l1.getClass()==l2.getClass());
不管泛型的类型形参传入哪种实参,对Java来说依然当成同一类来处理,在内存中也只占一块内存。因此在静态方法、静态初始化块,静态变量的声明和初始化中不允许使用类型形参。
public class R
{
//下面代码错误,不能在静态Field声明中使用类型形参
static T info;
//下面代码错误,不能在静态方法声明中使用类型参数
public static void bar(T msg){}
}
Java泛型提供了被限制的泛型通配符,被限制的通配符表示如下,
//它表示所有shape泛型List的父类
List extends Shape>
在一种更极端的情况下,程序需要为类型形参设定多个上限(之多一个父类上限,可以多个接口上限),表明该类型形参必须是其父类的子类(父类本身也行),并且实现多个上限接口(接口上限必须位于类上限之后)。代码如下:
//表明T必须是Number类或其子类,并且必须实现Serializable接口
public class Apple
20.泛型方法和通配符。