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)不要过多的使用反射。反射是很脆弱的,编译器很难发现程序中的错误,只有在运行时发现,并导致错误或异常。