前言
有了对象不知道如何面对?今天就手把手教你迈出勇敢的第一步:面向对象(Object Oriented Programming,OOP)。
一、封装
封装是处理对象的一个重要概念,使用private关键字将属性进行封装(这个属性只在当前类内部可见,对外部隐藏)。
被封装后的属性和方法不能被随意的调用和修改,只能通过get 和set两个方法来对属性进行操作。而被private修饰的方法需要通过Java中的类反射机制,我们暂且不提。
public class Animal{
private int age ;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
二、继承
1.继承的概念
- 发生在类和类之间,当类和类之间满足 is a 关系,即 一个类is a 另外一个类,一定存在继承关系,天然继承的。
比如说:现在有两个类一个Animal类和Dog类,Dog is an - Animal,此时Dog类就可以是Animal的子类。Animal就被称作父类、超类、基类,Dog就被称作子类、派生类。
- 用extends关键字表示继承。 一个子类只能继承一个父类,而一个父类可以被多个子类继承。
- 当一个子类继承了父类,父类中的属性和方法子类就天然具备了。 不能随便继承,必须满足is a的关系才可以使用继承。比如:Cat和Dog,这两个就不满足继承关系,因为Cat is not a Dog。但是Cat可以继承Animal,因为猫天然的是一个动物。
- Dog可以被其他类继承,比如其他品种的狗就可以继承Dog这个类。
代码如下(示例):
public class Animal{}
class Dog extends Animal{}
class Cat extends Animal{}
class Chaiquan extends Dog{}
2.继承使用的规则
- 要能使用继承,前提是必须满足类之间的is a关系。
- 一个子类只能使用extends关键字继承一个父类(单继承)。Java中不允许多重继承,extends后面只能跟一个父类,但是可以多层继承,比如:class Chaiquan extends Dog{}。
- 子类会继承父类所有属性和方法,显式继承(public属性和方法可以直接使用),隐式继承(private属性和方法),子类也继承了这个属性和方法,但是无法直接使用,必须通过父类提供的方法来操作。
- protected权限:同包下的没有关系的类之间以及不同包的有继承关系的类之间可见。
- 当产生一个子类对象时,默认会先产生父类对象,继而调用父类的构造方法。
public class Aniaml{
public Animal() {
System.out.println("1.父类Animal的无参构造");
}
}
public class Dog extends Animal{
public Dog() {
System.out.println("2.子类Dog的无参构造");
}
public static void main(String[] args){
Dog dog = new Dog();
}
}
运行结果如下:
当调用new Dog无参构造产生对象之前,先默认调用父类的构造方法产生父类对象,然后才会执行子类的构造方法。
我们复习一下程序的调用顺序,程序是从主方法开始执行的,但是主类是先加载的,主类一加载然后才执行主方法,如果主类有静态块,先执行静态块,如果有继承关系,先加载父类,再加载子类。类加载结束后才进入主方法。产生子类对象时,先要产生父类对象,如果有构造块先调用构造块,下来才是构造方法。
3.super关键字
- super修饰属性表示从直接父类中寻找同名属性,若没找到,再向父类的父类中找,以此类推。
注意和this关键字的区别,this是直接从当前类中找同名属性。 - super修饰构造方法 super(父类构造方法的参数); ,
- super也和this关键字一样,必须放在当前构造方法中的首行。在一个构造方法中无法同时显式使用this和super。
- super修饰普通方法。
- 当一个父类中存在有参构造时,此时父类的无参构造不会默认生成,子类有参构造方法的首行必须使用super(有参构造); 。
- super不能指代当前父类的对象引用。
super修饰属性:
public class Animal{
protected String name = "张三"
public Animal(String name) {
System.out.println("1.Animal的有参构造" + name);
this.name = name;
}
}
public class Dog extends Animal{
public Dog() {
super("狗子");
System.out.println("2.子类Dog的无参构造"+super.name);
}
public static void main(String[] args) {
Dog dog = new Dog();
}
}
运行结果如下:
super修饰方法:
public class Animal{
protected String name = "小动物"
protected void play() {
System.out.println("Animal的play方法");
}
public Animal(String name) {
System.out.println("1.Animal的有参构造" + name);
}
}
public class Dog extends Animal{
public Dog(String name) {
super(name);
System.out.println("2.Dog的有参构造");
}
protected void test() {
super.play();
}
public static void main(String[] args) {
Dog dog = new Dog("柴犬");
dog.test();
}
}
运行结果如下:
4.final关键字
- final修饰属性,表示该属性值不能变,是一个常量。
- final修饰类表示这个类无法被继承。 比如:final class Person,Person就无法被继承。
三、多态
1.向上转型
多态:一个引用可以表现出多种行为/特性,这就叫做多态性。作用是参数统一化,降低使用者的使用难度,将父类引用当作一个方法的参数,通过父类的引用去指代所有的子类。
语法:父类名称 父类引用 = new 子类实例(); 这叫做向上转型
Animal animal = new Dog();
假如我们没有向上转型的话,我们要想让一个方法表现出不同子类的方法行为,那我们就得重载很多次。
public class Animal {
protected void eat() {
System.out.println("Animal的eat方法");
}
public static void main(String[] args){
Dog dog = new Dog();
Cat cat = new Cat();
fun(dog);
fun(cat);
}
public static void fun(Dog dog) {
dog.eat();
}
public static void fun(Cat cat) {
cat.eat();
}
}
如果此时有向上转型,我们就可以直接用父类引用当作参数:
public class Animal{
protected void eat() {
System.out.println("Animal的eat方法");
}
public static void main(String[] args){
Animal animal = new Animal();
Dog dog = new Dog();
Cat cat = new Cat();
animal.fun(animal);
animal.fun(dog);
animal.fun(cat);
}
public void fun(Animal animal) {
animal.eat();
}
}
当我们animal有一个新的子类时是非常容易扩展的!
fun方法中animal局部变量的引用调用eat方法时,当传入的对象不同,表现出来的eat方法行为也不同,**(前提是该子类重写了父类的该方法)**这就是多态性。
向上转型后能通过”.”操作哪些方法,看的是类名称,就是前面的Animal说了算,至于方法表现出来到底是什么样子看的是new后面的子类,该子类是否重写了父类的该方法。如果没有重写,则会就近匹配,从直接父类中开始寻找第一个碰到的
2.方法重写(override)
首先先弄清方法重载和重写的区别:
方法重载:发生在同一个类中,定义了若干个方法名称相同 ,参数列表不同,和返回值类型无关的。
方法重写:
- 发生在有继承关系的类之间,子类定义了和父类出了权限不同其他全都相同的方法,返回值完全相同或者至少是向上类型的返回值,这样的一组方法称之为方法重写。
- 权限不同指得是子类重写该方法的权限必须大于等于父类才可以重写。
- 如果父类的方法被private所修饰,那么该方法不能被重写,因为此时该方法只在父类中可见,出了父类,外部都不知道有这个方法,何来重写一说。
- 我们可以使用注解 @Override 来检查是否重写成功。
- 方法重写只发生在普通方法中。
- static方法不能被重写,因为static和对象无关,而多态的本质就是调用了不同子类的对象,这些子类对象所属的类覆写(重写)相应的方法,才能表现出不同的行为。
public class Animal{
public void eat() {
System.out.println("Animal的eat方法");
}
public static void main(String[] args){
Animal animal = new animal();
Dog dog = new Dog();
Cat cat = new Cat();
animal.fun(animal);
animal.fun(dog);
animal.fun(cat);
}
public static void fun(Animal animal) {
animal.eat();
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("Dog的eat方法");
}
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("Cat的eat方法");
}
}
向上转型发生的时机:
- 引用赋值时
- 方法传参时
- 方法返回值(不常用)
public static Animal test() {
Dog dog = new Dog();
return dog;
}
3.向下转型
向上转型后,我们只能调用父类中存在的方法,我们要想调用子类独有的方法就要发生向下转型。
//向上转型:
Animal animal = new Dog();
//向下转型:
Dog dog = (Dog) animal;
//再转回去
Animal animal1 = dog;
- 向下转型是强制类型转换,因为子类一定是一个父类,但是父类不一定是该子类。
要发生向下转型,首先要发生向上转型。两个毫无关系的类之间的是无法进行转换的,比如猫和狗。虽然它两都是Animal的子类,但是它两不存在继承关系。 - 向下转型时会有类型转换异常风险(ClassCastException),我们可以使用instanceof关键字
引用名称 instanceof 类 返回布尔值,表示该引用是不是该类的对象
我们可以和分支语句搭配使用:
Animal animal1 = new Animal();
Animal animal2 = new Dog();
if (animal1 instanceof Dog) {
Dog dog = (Dog) animal1;
System.out.println(animal1 + "successul");
}else {
System.out.println(animal1 + "不是指向Dog类型引用");
}
if (animal2 instanceof Dog) {
Dog dog = (Dog) animal2;
System.out.println(animal2 + "successul");
}else {
System.out.println(animal2 + "不是指向Dog类型引用");
}
}
总结
个人知识有限,文中有不足之处恳请指出,谢谢!