超类与子类
在设计类的时候,应该将通用的方法放在超类中,将特殊用途的方法放在子类中。
超类的私有域
子类如果要访问超类的私有域,必须用超类提供的公有接口来访问。
子类构造器
super语句可以调用超类的构造器,super语句必须在第一条的位置上(this也是)。如果没有写super,则会调用超类的不带参数的构造器,如果超类没有,则会报错。
多态
可以将子类赋给超类,但不能将超类赋给子类。
子类方法覆盖超类方法
1.要保证方法的签名(方法名和参数列表)要相同,还有返回类型的兼容性,允许覆盖的返回类型为原返回类型的子类型。
2.子类方法不能低于超类方法的可见性。
final类
用final 修饰的类不能扩展子类,并且final类中的所有方法被自动转换为final 方法,但final 类的域不会被转换。
用final修饰类中的某个方法,则子类不能覆盖这个方法。
将类声明为 final 主要目的是: 确保它们不会在子类中改变语义。
超类转换成子类
如果要把超类转换成子类,可以用instanceof 进行检查。实际类型为子类才可以进行转换。只有在需要使用子类特有的方法时才进行这种转换。所以可以不进行转换,而是在超类中添加同名的方法,这样做更好(利用多态性)。
抽象类
1.一个类中只要包含一个抽象方法,那么这个类就必须被定义为抽象类(不含抽象方法也可以用定义成抽象类)。如果是继承来的抽象方法,除非把全部抽象方法覆盖掉,否则也必须定义成抽象类。
2.不能构造抽象类的实例 。可以定义一个抽象类的对象变量,但是只能构造它非抽象子类的实例,所以也只能使用非抽象子类中的方法。
访问修饰符
- private : 仅对本类可见。
- public : 对所有类可见。
- protected : 对本包和所有子类可见。
- 默认(不需要修饰符):对本包可见。
Object中的方法
equals方法
==和equals方法
==比较基本类型数据,equals比较两个对象状态。
Object中的equals方法是比较存储位置的,与==一样。所以在比较两个对象的内容时,应该对Object的方法进行覆盖。
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方法
每个对象都有一个默认的散列码
默认的散列码为对象的存储地址。
Objects.hashCode(a)是null安全的,当a为null时,返回0。
可以用Objects.hash( )来组合多个参数的散列值。
toString 方法
- 用一个字符(甚至是空字符串)与一个对象连接,就会自动调用toString方法。
- 用输出流打印任意一个对象时,也会自动调用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;反之,则会出现警告,使用强制类型转换,也是不可避免的,所以在转换时要谨慎。
对象包装器与自动装箱
对象包装器Integer,Long,Float,Double,Short,Byte,Character,void,Boolean.
```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();
}
}
}