《Java核心技术卷1》第五章笔记


超类与子类

在设计类的时候,应该将通用的方法放在超类中,将特殊用途的方法放在子类中。

超类的私有域

子类如果要访问超类的私有域,必须用超类提供的公有接口来访问。

子类构造器

super语句可以调用超类的构造器,super语句必须在第一条的位置上(this也是)。如果没有写super,则会调用超类的不带参数的构造器,如果超类没有,则会报错。

多态

可以将子类赋给超类,但不能将超类赋给子类。

子类方法覆盖超类方法

1.要保证方法的签名(方法名和参数列表)要相同,还有返回类型的兼容性,允许覆盖的返回类型为原返回类型的子类型。

2.子类方法不能低于超类方法的可见性。

final类

  • 用final 修饰的类不能扩展子类,并且final类中的所有方法被自动转换为final 方法,但final 类的域不会被转换。

  • 用final修饰类中的某个方法,则子类不能覆盖这个方法。

  • 将类声明为 final 主要目的是: 确保它们不会在子类中改变语义。

超类转换成子类

如果要把超类转换成子类,可以用instanceof 进行检查。实际类型为子类才可以进行转换。只有在需要使用子类特有的方法时才进行这种转换。所以可以不进行转换,而是在超类中添加同名的方法,这样做更好(利用多态性)。

抽象类

1.一个类中只要包含一个抽象方法,那么这个类就必须被定义为抽象类(不含抽象方法也可以用定义成抽象类)。如果是继承来的抽象方法,除非把全部抽象方法覆盖掉,否则也必须定义成抽象类。

2.不能构造抽象类的实例 。可以定义一个抽象类的对象变量,但是只能构造它非抽象子类的实例,所以也只能使用非抽象子类中的方法。

访问修饰符

  1. private : 仅对本类可见。
  2. public : 对所有类可见。
  3. protected : 对本包和所有子类可见。
  4. 默认(不需要修饰符):对本包可见。

Object中的方法

equals方法

==和equals方法

  1. ==比较基本类型数据,equals比较两个对象状态。

  2. Object中的equals方法是比较存储位置的,与==一样。所以在比较两个对象的内容时,应该对Object的方法进行覆盖。

  3. objects.equals(a,b)不仅可以判断两个对象是否相等,还可以在a,b为null的情况下进行判断。只有一个为null时,会返回false;两个都为null,会返回true.

getclass与instanceof 实现equal方法

当子类有自己的语义时,应当使用getclass去检测;而子类有统一的语义时,应当使用instanceof进行检测。这是因为getclass会严格判断类型,而不会考虑继承的情况;而instanceof 会判断是否属于该类或该类的子类,会考虑继承的情况。

public class Test
{
	public static void testInstanceof(Object x)
	{
		System.out.println("x instanceof Parent:  "+(x instanceof Parent));
		System.out.println("x instanceof Child:  "+(x instanceof Child));
		System.out.println("x getClass Parent:  "+(x.getClass() == Parent.class));
		System.out.println("x getClass Child:  "+(x.getClass() == Child.class));
	}
	public static void main(String[] args) {
		testInstanceof(new Parent());
		System.out.println("---------------------------");
		testInstanceof(new Child());
	}
}
class Parent {
 
}
class Child extends Parent {
 
}
/*
输出:
x instanceof Parent:  true
x instanceof Child:  false
x getClass Parent:  true
x getClass Child:  false
---------------------------
x instanceof Parent:  true
x instanceof Child:  true
x getClass Parent:  false
x getClass Child:  true
*/

hashCode方法

  1. 每个对象都有一个默认的散列码

  2. 默认的散列码为对象的存储地址。

  3. Objects.hashCode(a)是null安全的,当a为null时,返回0。

  4. 可以用Objects.hash( )来组合多个参数的散列值。

toString 方法

  1. 用一个字符(甚至是空字符串)与一个对象连接,就会自动调用toString方法。
  2. 用输出流打印任意一个对象时,也会自动调用toString方法。

getClass().getName()

可以用此方法获取类名的字符串。

泛型数组列表

泛型类Arraylist

ArrayList<Employee> staff = new ArrayList<>();//括号里可以定义初始容量
staff.ensureCapacity(100);//也可以这样定义初始容量
staff.size();//返回staff中实际元素个数,而不是容量
staff.trimToSize();//回收多余的容量
staff.set(i,harry);//对第i个元素赋值
Employee e = staff.get(i);//访问第i个元素
staff.add(harry);//在末尾添加一个元素
staff.add(i,harry);//在第i个位置上插入一个元素,原来这个位置上的元素以及后面的元素往后推
Employee e = staff.remove(i);//删除第i个位置上的元素,并返回该元素,后面的元素往前移,数组大小减一
for (Employee e : staff)
dosomethingwith e;       //可以使用for-each循环

类型化与原始类型

如Arraylist,一个类型化的Arraylist (带尖括号的)可以赋给一个原始类型的Arraylist;反之,则会出现警告,使用强制类型转换,也是不可避免的,所以在转换时要谨慎。

对象包装器与自动装箱

  1. 对象包装器Integer,Long,Float,Double,Short,Byte,Character,void,Boolean.

  2. ```java
    ArrayListlist = new ArrayList<>();
    list.add(3);//这条语句将自动变换成list.add(Integer.valueOf(3))自动装箱
    int n = list.get(i);//这条语句将自动变换成int n= list.get(i).intValue(),自动拆箱
    Integer a = 3;
    a++;//在算术表达式中完成了自动装箱和拆箱

    
    3. ```java
       Integer a = 1000;
       Integer b = 1000;
       if(a==b) ...;
       /*自动装箱规范要求int在-127到127之间。如上,a和b的int值为1000时,是不会自动拆箱的,因为==可以比较两个对象;而值为100时,这显然是成立的。

参数数量可变的方法

…表明这个方法可以接收任意数量的对象(fmt参数除外)。

public class Main {
	public static double max(double... value) {
		double a = Double.NEGATIVE_INFINITY;
		for (double v : value)
			if (v > a)
				a = v;
		return a;
	}

	public static void main(String[] args) {
		System.out.println(max(10.1, 29.1, 10.6, 5.1));
	}
}
public class Main {
	public static double max(double[] value) {
		double a = Double.NEGATIVE_INFINITY;
		for (double v : value)
			if (v > a)
				a = v;
		return a;
	}

	public static void main(String[] args) {
		System.out.println(max(new double[] { 10.1, 29.1, 10.6, 5.1 }));
	}
}

枚举类

public enum Size { SMALL , MEDIUM, LARGE, EXTRAJARGE };
//实际上, 这个声明定义的类型是一个类, 它刚好有 4 个实例(实例的值只能是int或者char),在此尽量不要构造新对象
System.out.println(Size.SMALL.toString( ));//返回"SMALL",即字符串形式的枚举常量
Size s = Enum.valueOf(Size.class, "SMALL");//toString的逆方法
Size[] values = Size.values( );//返回一个包含所有枚举常量的数组

反射

Class类

获取Class类对象

import java.util.Random;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
        /*第一种方法*/
		Random generator = new Random();
		Class a = generator.getClass();
		System.out.println(a.getName());
        /*第二种方法*/
		String classname = "java.util.Random";
		Class b = Class.forName(classname);
		System.out.println(b.getName());
        /*第三种方法*/
		Class c = Random.class;
		System.out.println(c.getName());
		Class d = int.class;
		System.out.println(d);
	}
}
/*一个 Class 对象实际上表示的是一个类型,而不仅仅是一个类,比如int.class*/

利用反射分析类

import java.util.Random;

public class Main {

	public static void main(String[] args) {
		Class cl = Random.class;
		System.out.println(cl.getModifiers());//返回描述类修饰符的int值
		System.out.println(cl.getFields());//返回域的信息
		System.out.println(cl.getMethods());//返回方法的信息
		System.out.println(cl.getConstructors());//返回构造器的信息
        /*以上三种会包括超类的公有成员,而getDeclaredFields,getDeclaredMethods,getDeclaredConstructors不会包括超类的成员*/
	}
}

运行时反射分析对象

public class Main {

	public static void main(String[] args) {
		Px p = new Px(1);
		Class cl = p.getClass();
		try {

			Field f = cl.getDeclaredField("name");// 初始化域
			Object v = f.get(p);// get方法查看对象域
			System.out.println(v);

			Field g = cl.getDeclaredField("y");
			Object w = g.get(p);// 此时右值为基础类型int,不是对象,但被打包到包装器Integer里去
			System.out.println(w);

			Field h = cl.getDeclaredField("x");
			h.setAccessible(true);//因为x是private,所以需先设置可访问标志
			Object z = h.get(p);
			System.out.println(z);
            
            h.set(p, 3);// 给对象里的h域设置新值
			z = h.get(p);
			System.out.println(z);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Px {
	private int x;
	protected int y = 2;
	protected String name = "hello world";

	public Px(int x) {
		this.x = x;
	}
}

用反射编写泛型数组代码

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;

public class Main {
	public static Object goodCopyOf(Object a, int newLength)
	{
	Class cl = a.getClass();
	if (!cl.isArray()) return null ;//判断是否为数组
	Class componentType = cl .getComponentType();//获取数组的类型
	int length = Array.getLength(a);//获取长度
	Object newArray = Array.newInstance(componentType, newLength);//动态地创建数组实例
	System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));//拷贝
	return newArray;//因为要兼容int等基础类型,返回类型应为Object,而不是Object[],因为int[]可以转换成Object,不能转换成Object[]
	}
	public static void main(String[] args) {
	      int[] a= new int[10];
	      a=(int[]) goodCopyOf(a,a.length);
	}
}

System.arraycopy的用法

System.arraycopy(int[] arr, int star,int[] arr2, int start2,int length);

第一个参数是要被复制的数组

第二个参数是被复制的数字开始复制的下标

第三个参数是目标数组,也就是要把数据放进来的数组

第四个参数是从目标数据第几个下标开始放入数据

第五个参数表示从被复制的数组中拿几个数值放到目标数组中

注意:length不能超过源数组长度(不能像copyOf一样扩展)

比如:数组1:int[] arr = { 1, 2, 3, 4, 5 };
数组2:int[] arr2 = { 5, 6,7, 8, 9 };
运行:System.arraycopy(arr, 1, arr2, 0, 3);
得到: int[] arr2 = { 2, 3, 4, 8, 9 };

调用任意方法

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
	public static void main(String[] args) {
		try {
			Method f = Math.class.getMethod("sqrt", double.class);
			/*
			 * 将f设置为Math里的一个方法 这里需要提供方法名和参数列表,因为可能存在多个相同名字的方法
			 */
			for (double i = 0; i <= 20; i++) {
				double y = (double) f.invoke(null, i);// invoke返回的是Object
				// 调用静态函数传null就可以了,否则传对象
				System.out.println(y);
			}
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

文章作者: 淡夜
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 淡夜 !
评论
  目录