JAVA-chapter5继承
子类引用可以赋值给父类引用,父类引用需要类型转换才能赋值给子类引用。
1从子类强制类型转换为父类时需要使用instanceof
if(staff[i] instanceof Manager){
boss=(Manager)staff[i];
...
}
5.2 Object类
Object 类是所有类的父类,所以可以使用:
Object obj =new Employee("Harry",35000);
所有非基本数据类型都是Object。(number,char,boolean)
5.2.2 equals方法
public class Employee{
public boolean equals(Object otherObject){
if(this==otherObject)
return true;//对象指向同一引用
if(otherObject ==nuill)r
return false;
if(getClass()!=otherObject.getClass())
return false;//属于不同类
Employee other=(Employee)otherObject;
return name.equals(other.name)&&salary==other.salary&&hireDay.equals(other.hireDay);
}
}
子类继承父类的equals方法并扩展
public class Manager extends Employee{
public boolean equals(Object otherObject){
if(!super.equals(otherObject))
return false;
Manager other=(Manager)otherObject;
return bonus==other.bonus;
}
}
5.3.1ArrayList
ArrayList
使用add方法
staff.add(new Employee("Harry Hacker",...));
知道大概数组数目
staff.ensureCapacity(100);
可以确保前100个对象不会引起数组长度的变化
size方法,返回数组的元素数目
staff.size()
trimToSize()
只有你保证不会向ArrayList里添加元素在使用trimToSize()
set方法,设置元素
staff.set(i,harry);//i index ,harry Employee
要保证使用set时index的值小于当前ArrayList长度
var list=new ArrayList<Employee>(100);//capacity 100 size 0
list.set(0,x);//wrong no element 0 yet
remove方法,移除元素
Employee e=staff.remove(n);
如果频繁在中部插入删除元素,建议使用链表Linked list
可以使用foreach语句
for(Employee e: staff)
do something with e
包装类
不要使用==比较两个包装值是否相等,要使用equals()方法。
下面是错误的范例
Integer a=1000;
Integer b=1000;
if(a==b)..
不要使用包装类的构造函数
Integer.valueOf(1000);//可以
new Integer(1000);//不要使用
转换字符串到整数
int x=Integer.parseInt(s);
...运算符
printf利用了...可以接受任意长度的参数
public static double max(double... values){
double largest=Double.NEGATIVE_INFINITY;
for(double v: values)if(v>largest)largest=v;
return largest;
}
调用
double m=max(3.1,40.4,-5);
5.6抽象类
抽象类用abstract修饰
抽象方法:public abstract String getDescription();
抽象类可以有域和具体实现。
抽象类不可以实例化。
可以创建抽象类的变量
Person P=new Student("Vince","Economics");
其中Person是抽象类,Student继承了Person
5.7 枚举类
1.枚举类型 public Enum Size{SMLL,MEDIUM,LARGE,EXTRA_LARGE}
枚举类型使用==进行比较,不使用equals()
2.枚举类
public enum Size{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
private String abbreviation;
Size(String abbreviation){
this.abbreviation=abbreviation;
}
public String getAbbreviation{return abbreviation;}
}
枚举的构造函数是私有的。
所有的枚举类型是抽象类Enum的子类。可以使用toString()
Size s=Enum.valueOf(Size.class,"SMALL");
枚举类有静态的values方法返回数组
Size[] values=Size.values();
ordinal方法返回enum的计数值,从0开始。Size.MEDIUM.ordinal()返回1
5.8封闭类 sealedclass
只允许指定类继承。其他类不允许继承。例
public abstract sealed class JSONValue
permits JSONArray,JSONNumber,JSONString,JSONBoolean,JSONObject,JSONull{
...
}
不允许的类无法继承,例
public class JSONComment extends JSONValue{}//Error
允许的子类必须是可达的。他们不能为私有类,或者是嵌套类,或在另一个包里的包可见类。
public的子类必须和sealed类在同一个包内。但如果你使用模块,他们必须在同一模块内。
5.9反射
getClass()方法返回类的类型
Employee e;
if(e.getClass()==Employee.class)...
这种判断方式只适用于e为Employee的类,若是e是Employee的子类会返回false.
如果你获得了一种类型,你可以创建实例,调用getConstructor().newInstance()
var className="java.util.Random";
Class c1=Class.forname(className);
Object obj=c1.getConstructor().newInstance();
如果该类没有不带参数的构造函数会抛出异常。
5.9.4使用反射分析类的能力
在java.lang.reflect包中的Field,Method,Constructor 三个类定义了类中的field,method,constructor
这个三个类都有getName()方法返回项目名称。
Field类中的getType方法,返回对象。方法类Method和构造函数类Constructor有方法可以获得参数的类型。
三个类还有getModifier方法返回一个整数,由很多bit位组成,判断修饰符的使用情况。使用Modifer类中的isPublic,isPrivate,isFinal来判断方法或者类
是否有这些属性。
Class类中有getFields,getMethods,getConstructors方法,返回公有的field,method,constructor数组。包含父类的共有成员。
getDeclaredField,getDeclareMethods,getDeclaredConstructors方法返回类声明的field,method,constructor包括私有,包,保护成员。不包括父类的成员
5.9.5利用反射在运行时分析对象
var harry=new Employee("harry Hacker",50000,10,1,1989);
Class cl=harry.getClass();//Employee
Field f=cl.getDeclaredField("name");//the name field of the Employee class
Object v=f.get(harry);//String object value "harry Hacker"
当对象是private 时会抛出IllegalAccessException
使用setAccessible(true);
f.setAccessible(true);//now Ok to call f.get(harry)
setAccessible是AccessibleObject类的方法。而AccessibleObject是Field Method Constuctor类的超类。
使用variable handle代替反射
public Object getFieldValue(Object obj,String fieldName,Lookup lookup)
throws NoSuchFieldException,IllegalAccessException
{
Class<?> cl=obj.getClass();
Field field =c1.getDeclaredField(fieldName);
VarHandle handle= MethodHandles.privaateLookupIn(cl,lookup).unrefectVarHandle(field);
return handle.get(obj);
}
5.9.6利用反射实现任意类型数组拷贝。
1.获得需拷贝数组a的类型Class
2.确认该Class是一个数组
3.使用getComponentType方法获得数组的正确类型
4.拷贝
代码如下
public static Object goodCopyOf(Object a,int newLength){
Class cl=a.getClass();
if(!cl.isArray())return null;
Class componentType =c1.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[] a={1,2,3,4,5};
a=(int[])goodCopyOf(a,10);
5.9.7调用任意方法和构造函数
Method类中有一个invoke方法,可以让你调用Method对象中的方法。
Object invoke(Object obj,Object...args)
第一个参数是暗含参数,后面的是明确的参数。
对于静态方法,第一个参数为null
例,m1代表了Employee类的getName方法,可以调用它为
String n=(String)m1.invoke(harry);
如果返回值类型是基础类型,那么invoke会返回它的包装类。
获得Method对象:
1.getDeclaredMethods ,从返回的数组中找寻要调用的对象。
2.调用Class类中的getMethod方法。
Method getMethod(String name,Class...parameterTypes)
例:
Method m1=Employee.class.getMethod("getName");
Method m2=Employee.class.getMethod("raiseSalary",double.class);
调用任意构造函数,使用Class.getConstructor方法。然后使用Constructor.newInstance
class cl=Random.class;
Constructor cons=cl.getConstructor(long.class);
Object obj=cons.newInstance(42L);