您的位置:首页 > 编程语言 > Java开发

Java基础之继承

2015-11-12 00:00 357 查看
1,利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上添加一些新的方法和域,以满足新的需求(is-a)。

2,反射是指程序运行期间发现更多的类及其属性的能力。

3,overload重载,override覆盖

4,super关键字与this关键字区别:

1)this关键字相当于对象的引用;

2)super不是引用,不能将super赋给一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字;

3)this一是引用隐式参数,二是调用该类的其它构造器;

4)super一是调用超类方法,二是调用超类的构造器。

5,子类中可以增加数据域、方法,或覆盖父类的方法,但是不能删了继续的数据域或方法;

6,子类构造器可以使用super()显示调用父类的构造器,如果子类不显示调用,则子类会默认调用父类的无参构造器;

如果子类没有显示的调用父类的构造,但父类又没有默认、无参构造,则编译器会报错。

7,一个对象变量(如e)可以指示多种实际类型的现象被称为多态,在运行时能够自己动地选择调用哪个方法的现象称为动态绑定。

public class Test {
public static void main(String[] args) {
Manager boss = new Manager("test", 8000, new Date());
boss.setBonus(5000);

Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("test1", 7000, new Date());
staff[2] = new Employee("test2", 6000, new Date());

for (Employee e : staff) {
System.out.println(e.getName() + "  " + e.getSalary());
}
}
}

public class Employee {

private String name;

private double salary;

private Date hireDay;

public Employee() {

}

public Employee(String name, double salary, Date hireDay) {
this.name = name;
this.salary = salary;
this.hireDay = hireDay;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

public Date getHireDay() {
return hireDay;
}

public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
}

public class Manager extends Employee {

private double bonus;

public Manager() {

}

public Manager(String name, double salary, Date hireDay) {
//初始化父类的构造,必须放在第一句
super(name, salary, hireDay);
this.bonus = 0;
}

public double getBonus() {
return bonus;
}

public void setBonus(double bonus) {
this.bonus = bonus;
}

public double getSalary() {
//super.getSalary() 调用父类的方法,只有Employee中的方法才能够访问其数据域
return bonus + super.getSalary();
}
}

8,对象变量是多态的,每个对象变量都属于一个类型;

9,动态绑定:

1)x.f(param),x为C类的对象,首先编译器会获得所有C类对象(即C类的父类、子类)所有public修饰的f()方法;

2)编译器查看调用方法的参数类型,如String类型参数,hello('test'),如果在父类或子类中存在多个类型相同的方法,编译器会报错,至此编译器与获得所有方法名和参数类型;

3)如果是static,private,final方法,编译器准确的知道调用哪个方法,这叫“静态绑定”;与之相对的是动态绑定;

4)程序运行时JVM一定调用与x所引用对象类型最合适的那类的方法,假设x的实际类型是D,D是C的子类,所以JVM会调用D的f()方法;

5)JVM每次运行时去会进行搜索,时间开销相当大,所以会建以一个方法表,需要时从方法表中读取。

10,子类覆盖父类方法时,访问权限不能低于父类的访问权限,如父类是public,子类也必须是public;反之编辑器会把它解释为试图降低访问权限;

11,阻止继承,final类、方法;

1)final类不允许被继承;如果类被声明为final,其类中的方法自动的转为final方法(不允许类继承,所以方法也是不能继承的),例如:String是final类,不允许继续;其作用主要是确保子类不会改变其语义。

2)final方法,子类不允许覆盖;

3)fina域,构造初始化对象过后不再允许更改它的值。

12,早期java中使用final关键字来避免动态绑定,如果一个方法较短并且没有被覆盖,编译器就会对其进行内联优化,例如:e.getNmae()将被优化成e.name;目前JVM的即时编译器比传统编译器处理能力强得多,可以对那些较短的方法,调用频率高的类进行自动优化,如果后期继承的子类对优化的方法有覆盖,优化器将取消内联优化,这个过程很慢,但很少发生。

13,类型转换,使用is-a原则,子类可以强制转换为基类,反之不行;ClassCastException

if(staff[1] instanceof Manager){
boss = (Manager)staff[1];
}

14,包含一个或多个抽象方法的类必须被声明为抽象类;抽象类同时包括具体数据和具体的方法;

abstract class Person{
private String name;

public abstract String getDescription();

public Person(String name){
this.name = name;
}

public String getName(){
return name;
}
}

15,类不包含抽象方法也可以声明为抽象,抽象类不可以被实例化。但可以定义抽象类的对象变量:

class Student extends Person{
}

//Pserson为抽象类,student为实现的子类
Person p = new Student("test", "test");

public abstract class Person {
private String name;

public abstract String getDescription();

public Person(String name) {
System.out.println("Person构造");
this.name = name;
}

public String getName() {
return name;
}

}

public class Student extends Person {

private String major;

//父类中存在有参构造,子类一定要先调用父类构造,如果是默认的,可以不调用super
public Student(String name, String major) {
super(name);
System.out.println("Student构造");
this.major = major;
}

@Override
public String getDescription() {
return "a student major in " + major;
}

}

public class Employee extends Person {

private double salary;
private Date   hireDay;

public Employee(String name, double salary, Date hireDay) {
super(name);
System.out.println("Employee构造");
this.salary = salary;
this.hireDay = hireDay;
}

@Override
public String getDescription() {
return String.format("an example with a salary of $%.2f", salary);
}

public double getSalary() {
return salary;
}

public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
}

public static void main(String[] args) {
Person[] persons = new Person[2];
persons[0] = new Student("student", "class");
persons[1] = new Employee("emplyee", 9000, new Date());
for (Person person : persons) {
System.out.println(person.getName() + ", " + person.getDescription());
}
}

输出结果:
Person构造
Student构造
Person构造
Employee构造
student, a student major in class
emplyee, an example with a salary of $9000.00

16,子类同样无法访问父类的private域;

17,protected方法比protected域更有实际意义,可以让子类调用父类的方法,其它包外的类不允许;

18,protected与defalut的区别:

1)protected,子类、包内访问权限,但这里有一点需要注意:如果子类在包外,也是可以访问的;


2)default,包内访问权限,没有子类这种定义,相当于只能在包内才能访问,只要在包外都不可以访问;

19,访问权限从大到小的顺序:

public->protected->default->private

20,Object类是java所有类的超类,java中的每个类都是由object类扩展而来,如果没有明确指出超类,Object就是这个类的超类;

//object只能持有引用,不能对内容进行操作
Object obj = new Employee("", "");

21,在java中只有基本类型不是对对象,例如:数值,字符,布尔型的值都不是对象,8种基本数据类型;

22,Object的equals比较的是两个对象引用是否相等

23,==比较基本数据类型, equals比较对象域(数据),如果都相等,则返回true;

24,hashcode,散列码,是由对象导出的一个整型值,两个对象的.hashCode()基本不会相同;

25,由于hashCode方法定义在Object类中,因此每个对象都有一个默认的hashcode,其值为对象的存储地址

26,

String s = "OK";
StringBuilder sb = new StringBuilder(s);
System.out.println(s.hashCode() + " " + sb.hashCode());
String t = new String("OK");
StringBuilder tb = new StringBuilder(t);
System.out.println(t.hashCode() + " " + tb.hashCode());

结果:
2524 14576877
2524 12677476

为什么s、t的散列码相同?因为String重写了Object的hashcode,导出内容的散列码;

然而sb、tb的散列码不同?因为StringBuilder没有重写Object的hashcode,采用了Object的默认方法,导出的是对象的存储地址。

27,equals与hashCode的定义必须一致:如果x.equals(y)返回true,那个x.hashCode()必须与y.hashCode()具有相同的值(存储地址);

Arrays.hashCode();

hashCode(null) = 0,hashcode可以为正数或负数;

28,toString(对对象来说x.toString()与""+x是等价的,但如果x是基本类型则会直接执行)

@Override
public String toString() {
return super.toString() + "Employee[name=" + super.getName() + ", salary=" + salary
+ ", hireDay=" + hireDay + "]";
}

29,toString打印数组

int[] array = {1,2,3,4,5,6};
System.out.println(Arrays.toString(array));
//打印多维数组
Arrays.deepToString();

30,ArrayList动态数组,在数组已满的情况下,会重新创建一个更大的数组,然后将较小的数组copy到较大的数组中;

1)数组列表管理着对象引用的一个内部数组;

2)JAVA7中,如果确定一个数组列表容量,可以提前分配数组的列表容量arraylist.ensureCapacity(100);同时也可以采用以下方式:

List<String> arrayList = new ArrayList<String>(100);

3)数组列表容量与数组大小有区别(new Employee[100]),分100个空间,说明数组有100个空位置可用;100个列表容量表示只是拥有保存100个元素的潜力,实际上,重新分配空间的话会超过100;

31,ArrayList与数组区别:

1)数组,固定长度,效率较ArrayList高;

2)ArrayList,动态数组,可变长度,无初始化数组长度,底层当然是用数组来实现;

3)ArrayList由于增加了灵活性,所以较数组效率低;

List<String> arrayList = new ArrayList<String>(100);
for (int i = 0; i < 20; i++) {
arrayList.add(String.valueOf(i));
}
//数组
String[] arrays = new String[arrayList.size()];
//转换为数组
arrayList.toArray(arrays);
System.out.println(Arrays.toString(arrays));
输出结果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]

32,每个基本数据类型都有一个包装器类,如:Integer,Long,Float,Double,Short,Byte,Void,Boolean,其中前6个派生于公共的超类Number。

1)Integer.valueOf(int),自动装箱

2)list.get(i).intValue(),自动拆箱

3)装箱和拆箱是编译器认可的,不是JVM

33,继承设计:

1)将公共操作、域放在超类;

2)不要使用受保护的域;protected,破坏封装,无限子类可以访问;同一个包中可以访问导致不是子类也可以访问;但需要在子类中重新定义的可以使用;

3)使用继承实现“is-a”关系,继承很容易达到节省代码的目的;

4)除非所有的继承方法都有意义,否则不要继承;

5)在覆盖方法时,不要改变预期的行为;置换原则不仅应用于语法,而且也可以应用于行为;

6)使用多态,而非类型信息(根据类型来判断使用哪个方法);

7)不要过多的使用反射。反射是很脆弱的,编译器很难发现程序中的错误,只有在运行时发现,并导致错误或异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: