《疯狂Java讲义》笔记

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代表对象本身,可以用来区分成员变量和局部变量。

super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)。
this(参数):调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)。
1)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
 
2super()this()类似,区别是,super从子类中调用父类的构造方法,this()在同一类内调用其它方法。
 
3super()this()均需放在构造方法内第一行。
 
4)尽管可以用this调用一个构造器,但却不能调用两个。
 
5thissuper不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
 
6this()super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。
 
7)从本质上讲,this是一个指向本对象的指针然而super是一个Java关键字。

3.引用变量在编译阶段只能调用其编译时类型所具有的方法。而运行时则调用它运行时类型所具有的方法。所以,编写java代码时,引用变量只能调用声明该变量时所用类里包含的方法。例如,通过Object p = new Person();定义变量p,则p只能调用Object类的方法,而不能调用Person类里定义的方法。
通过引用变量来访问其包含的实例变量Field时,系统总是试图访问它编译阶段所具有类型的Field,而不是运行时类型所定义的Field。

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

常量池(Constant Pool)专门用于管理在编译期间就被确定下来并被保存到.class文件中的数据。它包括了类、方法、接口中的常量,还包括字符串常量。


6.String:“hello”和new String("hello");的区别:当Java直接使用如“hello”这样的字符串直接量(包括可以在编译时就计算出来的字符串值)时,JVM使用常量池来管理“hello”直接量,再调用String构造函数产生一个对象,该对象保存在堆内存中,换句话说,new String("hello")一共产生了两个对象。

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对象可以访问它所属类的类成员。
9.final修饰的类Field,实例Field能指定初始值的地方如下,

类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修饰的方法");
	}*/
}

12.Scanner可以非常方便获取地获取键盘输入,它是Java5新增的工具。Java5之前,程序通常通过BufferedReader类来读取键盘输入。

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派生子类,则Apple中所有使用形参的地方都将替换为String,在派生类中重写父类方法时要注意这一点。

例如:ArrayList,系统并没有为ArrayList生成新的class,而且也不会把ArrayList当成新类来处理。

下面的代码输出true。

List l1 = new ArrayList();
List l2 = new ArrayList();
System.out.println(l1.getClass()==l2.getClass());

不管泛型的类型形参传入哪种实参,对Java来说依然当成同一类来处理,在内存中也只占一块内存。因此在静态方法、静态初始化块,静态变量的声明和初始化中不允许使用类型形参。

public class R
{
	//下面代码错误,不能在静态Field声明中使用类型形参
	static T info;
	//下面代码错误,不能在静态方法声明中使用类型参数
	public static void bar(T msg){}
}

如果Foo是Bar的子类型,G是具有泛型声明的接口或类,G并不是G的子类型

Java泛型提供了被限制的泛型通配符,被限制的通配符表示如下,

//它表示所有shape泛型List的父类

List

在一种更极端的情况下,程序需要为类型形参设定多个上限(之多一个父类上限,可以多个接口上限),表明该类型形参必须是其父类的子类(父类本身也行),并且实现多个上限接口(接口上限必须位于类上限之后)。代码如下:

//表明T必须是Number类或其子类,并且必须实现Serializable接口

public class Apple

20.泛型方法和通配符。


你可能感兴趣的:(Java)