java学习笔记(Core Java)5 继承
第五章 继承
1. 类 超类 子类
继承:extends (所有继承嗾使公有继承,没有私有继承和保护继承)
c+++: 用:
2.使用super(c#使用Base)调用基类的方法
子类构造器会默认调用基类的无参构造来初始化基类的私有域
如果基类没有无参构造,则会报错
3.this :1.引用隐式传参 2.调用类其他构造器
super:1.调用超类的方法 2.调用超类的构造器
c++使用初始化列表来调用基类的构造函数
4.java中,不需要将方法声明为虚拟方法方法,基类指针指向不同对象时,虚拟指针知道指针指向的实际对象类型,所以能正确调用对应的方法
如果不希望让一个方法具有虚拟特征,可以标记为final
5.java不支持多继承
6.多态:动态绑定、静态绑定和方法表
1)编译器准确知道调用哪个方法的叫静态绑定
(用private、static、final声明过的方法)
在运行时,调用方法依赖于实际类型并实现方法绑定(运行时可知)的叫动态绑定
虚拟机为基类和派生类生成一个方法表,虚拟机会在方法表中搜索最适合的方法进行调用
动态绑定的优点就是:对程序进行拓展时,不需要重新对调用方法的代码进行重新编译
方法重写时,子类的方法的访问性不能低于基类方法
7.阻止继承 final类
final修饰的类不能被继承
final修饰的方法不能被覆盖
final修饰的域在构造方法内初始化,并且不能被改动
被final修饰的类,其方法自动为final方法,但而不包括域
动态绑定对系统开销很大,所以即时编译器会自动为没有被覆盖的类标记为内联函数,减少对系统的开销
8.强制转换
1)只能在继承层次内进行转换
2)将基类转换为子类之后,需判断是否转换成功 使用instanceof运算符算符检查
Employee[] staff = new Employee[3];
java:Manager boss = (Manager)staff[1];
if(staff[1] instanceof Manager)//instanceof实例是否属于该类
...
c++: Manager* boss= dynamic_cast<Manager*>(staff[1])
if(boss!= null)
...
如果转换失败java不会生成一个空对象,而是抛出一个异常
补充: c++四种强制类型转换,所以c++不是类型安全的
static_cast:基本数据类型转换
const_cast:常量转化为非常量,或者反过来
reinterpret_cast:任意类型转换而无须考虑安全和常量问题,不要轻易使用
dynamic_cast:运行时转换,运行时要进行类型检查,存在继承关系之间(类中要有虚函数才能进行此转换,需检查类型信息,而类型信息保存在虚函数表中)
9.抽象类与抽象方法
抽象类包含抽象方法也可以包含具体数据、具体方法
类即时不含有抽象方法,也可以声明为抽象类
抽象类不能被实例化
c++:尾部=0的方法为纯虚函数,包含虚函数的类为虚类(抽象类) (虚函数可以有自己的实现,纯虚函数=0)
10.访问权限:
private:本类可见(也是c++初始化列表的原因,因为子类不可方位基类的private字段,需要调用基类的构造函数)
public:对所有类可见
protected:本包和所有子类可见(c++只对子类可见)
默认(无修饰符):对本包可见
11.object 超类
在java中,只有基本类型不是对象,其他类型(包括数组,枚举)都是对象,都扩展于超类
c++:void* ,每个指针都可以转化为void*指针
12.equal 仅比较了是否具有相同的引用
最好手动的重写equal方法
重写equals方法的要求:
1.自反性:对于任何非空引用x,x.equals(x)应该返回true
2.对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
3.传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
4.一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
5.非空性:对于任意非空引用x,x.equals(null)应该返回false。
重写equals方法的要点:(注意! 重写该方法)
@overrade public boolean equal(OBject otherObject)
1. 使用==操作符检查“实参是否为指向对象的同一个引用”。
if(this == otherObject)
2.判断实参是否为null
if(otherObject == null)
3. 使用instanceof操作符检查“实参是否来自是否同一个类”。
if(getClass()!= otherObject.getClass()) //是否同一个类
if(!(otherObject)instanceof ClassName)//如果所有子类拥有统一的语义
4. 把实参转换到正确的类型。
ClassName other = (ClassName)otherObject
5.对所有域依次比较:对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。对于既不是float也不是double类型的基本类型的域,可以使用==操作符进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法;对于float类型的域,先使用Float.floatToIntBits转换成int类型的值,然后使用==操作符比较int类型的值;对于double类型的域,先使用 Double.doubleToLongBits转换成long类型的值,然后使用==操作符比较long类型的值。
6.当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?(其他两个特性通常会自行满足)如果答案是否定的,那么请找到这些特性未能满足的原因,再修改equals方法的代码。
数组可以使用Arrays.equal来判断
////////////////////////////
java.util.Arrays
static Boolean equal(type[] a,type[] b);
static Boolean equal(object a,object b);
////////////////////////////
13.hashCode
每个类都有一个散列码
如果重写equal,也需要重写hashCode
14.toString 强烈建议自己的类重写提String方法,用于打印日志或者错误信息 例如:
public String toString()
{
return getClass().getClass
+ "[name=" + name
+ "age=" + age
+ "]";
}
println会直接调用x.toString方法
数组需要调用Arrays.toString方法
多维数组调用Arrays.deepString方法
/////////////////////////
java.lang.Object
Calss getClass() //返回包含对象信息的类对象
boolean equal()
String toString();
java.lang.Class
String getName();
Class getSuperclass() //返回超类信息
/////////////////////////
15.泛型数组 ArrayList<T> array = new ArrayList<T>()
1)c++:类似于vector,但是没有重载[]。再c++中,列表a=b时一种值拷贝,而在java中,a=b则a,b指向同一个列表
(1)add()
add(int index, T obj) //需要移动大量元素
(2)ensureCapacity(100) //如果可以估计存储数量,可以再填充前分配一个这么大的内部//数组空间
(3)size == 数组a.length()
(4)trimToSize() //如果确认列表大小不再增大,可以调用该方法清除多余的空间
5)set/get :由于没有重载[]运算符。。。。
set(int iT obj);//替换某个位置上的某个值(不是增加!)
T get(int i); //返回object,获取后需要强制转换
6)T remove(int index) //需要移动大量元素
7) x [] = new x[list.size()]
list.toArray(x) //导入数组
历史遗留问题:
ArrayList list=employee.find(oldlist)//参数列表元素可能有些不相符
由于早期ArrayList没有泛型,所以将一个原始的ArrayList赋值一个类型化的ArrayList会有警告,只要确保不会造成后果,可以使用@SuppressWarning("unchecked"),关闭此条警告
16拆箱与装箱
每个基本类型都有之对应的类(对象包装类)
Integer Long Float Double Short Byte Character Void Boolean(前六种继承Number类)
对象包装类不允许修改其中的值,同时也是final声明的
使用包装器声明变量时,其==表示其是否指向同一个对象,因此
两个对象包装器比较时调用equal方法
17.java如何改变传参的值??
C++:指针传参,引用传参 c#:使用 ref 标记实参与形参
java 使用持有者类型(IntHolder、BooleanHold等)
public static void triple(IntHolder x)
{
x.value = 3 * x.value; //可以改变传参的值
}
关于c++引用:
引用其实就是一个常量指针,同时也是用常量指针去实现的。引用占用四个字节。因为是常量指针,所以不能修改被引用的对象的内容。这比使用指针将更加安全
/////////////////////////////
java.lang.Integer
int intValue();//返回interger中的值
static String toString(int i);//将int包装为十进制String
static String toString(int i,int radix);//指定进制
static int parseInt(String s) //c# int.prase(String s)
static int parseInt(String s,int radix)
static Integer valueOf(String s)
static Integer valueOf(String s,int radix)
java.text.Number.Format
Number prase(String s); //假设s时一个数值
/////////////////////////////
18.可变参数 使用...
public static double max(double... value);
实际上将若干参数绑定在数组中传递该max函数
public static void main(String... args);
19.枚举 enum
实际上是一个类,包含若干个实例,因此两个枚举类型的值比较,直接使用 == ,永远不要调用equal。
所有的枚举类型都是Enum类的子类。所以他们继承了这个类的许多方法String toString() //返回枚举常量名
static Enum valueOf(Class enumClass,String name)//返回指定名字给定类的枚举常量
int ordinal() //返回在enum声明中枚举常量的索引 (从0 开始)
int compareTo(E other) //在枚举中的顺序比较
20反射与反射库
反射被大量用在javaBeans中。
反射的作用:
①在运行中分析类的能力
②在运行在查看类的对象
③实现通过的数组操作代码
④利用Method对象。 特别C++像函数指针(C#,委托与事件)
极其强大的构造工具!!!
(1)Class类
程序运行时,java运行系统始终为所有的对象维护一个被称为运行时的类型标识,这个信息跟踪着每个对象所属的类,虚拟机会利用运行时的类型信息选择相应的方法执行.Class类负责维护这些信息,每一个java类在虚拟机种都会表现出一个Class类 (这与Object中的getClass 返回类的实例容易混肴)
Employee e;
e.getName();//返回一个Class对象
e.getClass().getName();//返回类的名字
如果类在包里,在返回 包名+实例名
Data d = new Data();
Class c = d.getClass();
String name = c.getName(); // java.util.Data
使用静态方法forName获取类名对应的Class对象
String classname = "java.util.Data";
Class c = forName(classname);//该方法只有在classname时类名或者接口名中使用,否则会抛出异常 checked exception.使用该方法需要提供一个异常处理器
(可以先启动程序,然后使用forName加载所有的类,给用户一种启动很快的幻觉)
使用T.class方法获取匹配的类对象(T代表任意java类型)
Class c1 = Data.class
Class c2 = int.class
Class c3 = double[].class//历史原因,数组会返回一个比较奇怪的类名
总结:
三种方法获取Class类对象
实例名.getClass
任意类型T.class
forName(类名字符串)//这是一个静态方法 Class.forName(s);
Class Poin(){...}
Point p = new Point();
Class c1 = p.getClaa();
System.out.println(c1.getName()) // Point
Class c2 = Class.forName("Point")
System.out.println(c2.getName()) // Point
Class c3 = Point.class;
System.out.println(c3.getName()) // Point
每一个类的对象在虚拟机中都会生成一个Class类对象来保存这个每个类的信息,可以获取到这些信息进而获取到这个类的所有信息。
虚拟机为每一个类型管理一个Class对象,
可以用 == 比较两个类对象 if(e.getClass() == Employee.class) ...
获取实例后也可以反射创建对象 e.getClass().newInstance();
newInstance() 会调默认的构造方法,如果没有,将抛出异常
forName与newInstance() 根据类名生成对象
String s = "java.util,Data";
Data d = (Data)Class.forName(s).newINstance();
Constructor类中的newInstance()//提供构造器参数
////////////////////////////
使用printStackTrace打印出栈的轨迹
java.lang,Class
static Class forName(String classname);
Object newInstance();
java.lang,reflect.Constructon
Object newInstance(Object[] args);//带参构造
////////////////////////////