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 staff=new 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);
posted @ 2025-01-15 15:51  zhongta  阅读(7)  评论(0)    收藏  举报