面向对象的三大特征:封装、继承、多态
1.访问控制符
Java的访问控制符:private、protected、public,还有一个不加任何访问控制符的访问控制级别。
四个控制级别由小到大:private、default、protected、public
private:当前类访问权限
default:包访问权限,可以被相同包下的其他类访问
protected:子类访问权限,既可以被同一个包中的其他类访问,也可以被不同包中的子类访问
public:公共访问权限
对于局部变量而言,其作用于就是它所在的方法,不能被其他类访问,所以不能使用访问控制符修饰。
2.封装:将对象的状态信息对外部隐藏起来,不允许外部直接访问,而是由该类提供方法来实现对信息的访问。
良好的封装应该是,对象的Field和实现细节隐藏起来,外界无法直接访问,提供方法来对Field进行访问和操作。
如果一个Java类的每个Field都被使用private修饰,并为每个Field都提供了public修饰的setter和getter方法,那么这个类就是一个符合JavaBean规范的类。JavaBean是一个封装良好的类。
eg:
package cn.it.lsl; public class Person { private String name; private int age; public void setName(String name){ if(name.length()>6 || name.length()<2){ System.out.println("您设置的人名不符合要求"); return; }else{ this.name = name; } } public String getName(){ return this.name; } public void setAge(int age){ if(age>100 || age<0){ System.out.println("您设置的年龄不符合要求"); return; }else{ this.age = age; } } public int getAge(){ return this.age; } }
package cn.it.lsl; public class PersonTest { public static void main(String[] args) { Person p = new Person(); //p.age = 50; //错误。不能直接访问 p.setAge(1000); System.out.println(p.getAge()); //未能正确设置年龄 p.setAge(25); System.out.println(p.getAge()); } }
3.继承
1)Java中的继承是单继承,每个子类只有一个直接父类,但是可以有多个间接父类。继承是使用extends关键字来实现的。
注:Java的子类不能获得父类的构造器。
package cn.it.lsl; public class Fruit { public double weight; public void info(){ System.out.println("ifno()"); } } package cn.it.lsl; public class Apple extends Fruit{ public static void main(String[] args) { Apple a = new Apple(); a.weight = 30; a.info(); } }
2)子类包含与父类同名的方法,成为重写
重写应遵循:方法名相同,形参列表相同;
子类方法返回值类型应该比父类方法返回值类型更小或者相等
子类方法声明抛出的异常应比父类方法声明抛出的异常类更小或相等。
子类访问权限应比父类的访问权限更大或相等。
覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法,不能一个是类方法,一个是实例方法。
如果父类方法具有private访问权限,那么子类无法访问该方法,也就是无法重写该方法。
package cn.it.lsl; public class Apple extends Fruit{ public void info(){ System.out.println("子类的info()"); } public static void main(String[] args) { Apple a = new Apple(); a.info(); } }
将输出:子类的info()
3)super关键字
super用于限定该对象调用它从父类继承得到的Field或方法。super不能出现在static修饰
如果子类方法要调用父类被覆盖的方法,则可以使用super(被覆盖的是实例方法)或者父类类名(被覆盖的是类方法)作为调用者来调用父类中被覆盖的方法。
package cn.it.lsl; public class Apple extends Fruit{ public void info(){ System.out.println("子类的info()"); } public void callOverrideMethod(){ super.info(); } public static void main(String[] args) { Apple a = new Apple(); a.info(); a.callOverrideMethod(); } }
如果子类定义了和父类同名的Field,则会发生子类Field隐藏父类Field的情形。在正常情况下,子类里定义的方法直接访问该Field默认会访问到子类中定义的Field,无法访问到
父类中被隐藏的Field。在子类中定义的实例方法中可以通过super来访问父类中被隐藏的Field。
package cn.it.lsl; public class Apple extends Fruit{ public double weight = 15; public void accessOwner(){ System.out.println(weight); } public void accessFruit(){ System.out.println(super.weight); } public static void main(String[] args) { Apple a = new Apple(); a.accessOwner(); a.accessFruit(); } }
当程序创建一个对象的时候,系统不仅会为该类中定义的实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存,即使子类定义了与父类中同名的实例变量。
子类中定义与父类中同名的实例变量并不会完全覆盖父类中定义的实例变量,它只是简单地隐藏了父类中的实例变量。
package cn.it.lsl; class Parent{ public String tag = "aa"; } class Derived extends Parent{ private String tag = "bb"; } public class HideTest { public static void main(String[] args) { Derived d = new Derived(); //System.out.println(d.tag); //程序不能访问private修饰的Field System.out.println(((Parent)d).tag); } }
子类不会获得父类的构造器,但是子类构造器里面可以调用父类构造器的初始化代码
在一个构造器中调用另一个重载的构造器使用this,在子类构造器中调用父类构造器使用super
package cn.it.lsl; class Base{ public double size; public String name; public Base(double size, String name){ this.size = size; this.name = name; } } public class Sub extends Base{ public String color; public Sub(double size, String name, String color){ super(size,name); this.color = color; } public static void main(String[] args) { Sub s = new Sub(1.2, "aa", "红色"); System.out.println(s.size + "," + s.name + "," + s.color); } }
super调用父类构造器也必须出现在子类构造器中第一行,所以this和super不能同时出现。
不管我们是否使用super调用来执行父类构造器的初始化代码,子类构造器总会调用父类构造器一次。
当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行。