目录
封装
一、public访问修饰符
二、private访问修饰符
1、修饰属性
2、getter和setter
三、default访问修饰符
继承
一、父类和子类成员关系
1、父类和子类属性之间的调用
2、父类和子类方法之间的调用
二、父类与子类的构造方法
三、super修饰符
1、修饰属性
2、修饰方法
四、protected访问关键字
五、继承关系
多态
一、方法重写(override)
1、重写的定义
2、实现多态的条件
3、 向上转型
4、向下转型
二、final修饰符
1、修饰属性
2、修饰方法
3、修饰类
面向对象的三大特性:封装,继承与多态。
封装
强调的是保护性与易用性。
什么是封装:对外屏蔽类内部一些实现细节,类的外部只能通过类提供的方法操作类。
将数据和操作数据的方法进行结合,隐藏对象的属性和实现细节,对外部提供公开的方法进行交互。
如何实现封装:通过类结构以及访问修饰符来实现封装性。
四大访问修饰符:
private(私有权限,类内部权限)<default(包访问权限,包内部可见)<protected(继承权限)<public(公开的,所有对象可见)
一、public访问修饰符
当访问修饰符使用public在当前项目中任何地方都可以调动public权限的域(类、属性和方法)。
二、private访问修饰符
1、修饰属性
成为私有属性,这些属性只在当前类的内部可见,对外部不可见。
public class BankCard {private int cardNum;//卡号private String password;//密码private double balance;//余额public int test;//测试
}
例如在BankCard类中, 定义了三个私有属性和一个公开属性,在另一个类中只能访问公开属性,不能访问私有属性。私有属性对其他类完全隐藏。
2、getter和setter
当属性被private封装后,类中需要提供getter和setter方法来控制私有属性被外部访问,类的外部通过访问这两种方法来使用私用属性。
阿里编码规约:类中成员变量没有特殊情况,统一使用private进行封装,合理提供getter和setter方法。
getter是取得私有属性。
public BankCard(int cardNum, String password) {this.cardNum = cardNum;this.password = password;
}
public int getCardNum(){return cardNum;
}
public double getBalance() {Scanner sc = new Scanner(System.in);System.out.println("请输入您的账户密码:");String pass = sc.nextLine();if(pass.equals(this.password)){System.out.println("正在查询您账户的余额");return this.balance;}System.out.println("密码错误!");return -1;}
public class Test {public static void main(String[] args) {BankCard bankCard = new BankCard(12345,"123dgn");System.out.println(bankCard.getCardNum());System.out.println(bankCard.getBalance());}
}
创建bankCard对象赋予账号12345密码“123dgn”,然后获得卡号并且输出,然后取得该用户的私有属性余额,至于输入正确的密码才能获得余额输出。
setter是修改私有属性。
public void setPassword() {Scanner sc = new Scanner(System.in);System.out.println("请输入您的旧密码:");String oldPass = sc.nextLine();if(oldPass.equals(this.password)){System.out.println("请输入您的新密码:");String newPass = sc.nextLine();this.password = newPass;System.out.println("密码修改成功");}else{System.out.println("密码错误!");}}public String getPassword() {int num = 123456;Scanner sc = new Scanner(System.in);System.out.println("请输入您的验证码:");int number = sc.nextInt();if(number==num){return this.password;}return "验证码错误!";
}
public class Test {public static void main(String[] args) {BankCard bankCard = new BankCard(12345,"123dgn");System.out.println(bankCard.getPassword());bankCard.setPassword();}
}
同样创建bankCard对象赋予账号12345密码“123dgn”,通过getPassword可以输入系统定义的验证码来返回忘记的密码,然后再调用setPassword修改密码,先输入旧密码然后输入新密码,提示修改成功则成功修改密码。
三、default访问修饰符
什么是包:就是文件夹。(通常定义时名字全部小写,多个单词用_来分割)
不写关键字,权限修饰符不写就是包访问权限,被修饰的对象仅在包内部可见,只有在同级目录下的类之间可见,子包也不可见。
java中使用package来声明一个包,来解决类名称重复的问题。
若多个包下都存在同名的类,就要用import导入某个包下的某个类。(impoet是用来导入类,不是导入包)
静态导入:import static
就是将一个类的静态域导入到当前的类中。
继承
强调的是扩展性。
将多个类的一些共性抽取出来,提出了继承的概念,就是在进行共性提取,实现代码复用,增加整个程序的扩展性。方便进行子类,子功能的扩展,将所有共性的内容放在父类,子类只需要关注子类独有的属性和功能。
想使用继承必须满足 is a 原则。
修饰语法:class 子类名称 extends 父类名称{ }
当子类继承父类需要先初始化继承过来的父类的成员,此时需要通过super来完成。很明显在子类的构造方法当中,并没有通过super调用父类的构造方法。
一、父类和子类成员关系
1、父类和子类属性之间的调用
当访问的变量(不管类型)子类中存在的,此处的a和b在子类中存在,则子类中调用的a和b默认为子类中的属性,如果子类中不存在该属性,才会从父类中寻找同名属性。若父类中也找不到则编译报错。
public class Num {int a = 10;int b = 10;int c = 20;
}
class Number extends Num{char a ;char b ;public void Test(){a = 99;b = 100;c = 101;}
}
public class Test {public static void main(String[] args) {Number number = new Number();number.Test();System.out.println(number.a);System.out.println(number.b);System.out.println(number.c);}
}
Number为子类,Num为父类,子类和父类中都与a和b属性,只有父类中有c属性。所以调用子类中Test方法时,赋值给子类的a、b以及父类中的c。
如果Num中的a、b、c是私有属性private修饰的变量,name在Number中不能访问到三个变量所以会编译错误。这种继承是隐式继承。
2、父类和子类方法之间的调用
和属性的调用是一样的,当通过子类对象访问方法时,若子类中存在完全一样方法,则优先访问子类的同名方法,如果子类不存在该方法就调用父类的方法。
当子类和父类有一样名称的方法但是参数不一样时,要考虑具体方法和父类子类中同名方法哪一个参数一样进行调用。
二、父类与子类的构造方法
当产生子类对象时,默认先调用父类的构造方法来产生父类对象然后调用子类的构造方法产生子类的对象。
public class base {public base() {System.out.println("父类的无参构造");}public static void main(String[] args) {NewBase newBase = new NewBase();}
}
class NewBase extends base{public NewBase() {System.out.println("子类的无参构造");}
}
三、super修饰符
表示直接从父类中寻找成员变量或成员方法,private无法被调用。
1、修饰属性
表示直接寻找父类的同名属性。
如上述Number子类中test方法给a,b赋值时在前面加上super.则不会根据就近原则去给子类中a呵呵b赋值而去选择父类Num中的a和b变量赋值。
2、修饰方法
修饰普通成员方法,与属性基本原则相同。
修饰构造方法时,如果父类中没有无参构造,则在子类的构造方法首行必须显示的使用super(参数)来调用父类的构造方法。
在实例化子类对象时首先调用父类的构造方法先产生父类 对象而后使用子类构造产生子类对象。
在存在父类继承时,子类中不出现this()的构造方法调用。
四、protected访问关键字
在不同包的具有继承关系的类之间可见,同时在一个包内毫无关系的两个类之间也是可见的,包含比它小的包权限的权限。
五、继承关系
java的单继承局限:一个类只能使用extends直接继承一个父类,不允许多重继承。但是允许多层继承。不同的类也可以继承同一个类。
类和类之间的关系是:继承和组合。
判断继承和组合还是看类之间的关系:继承表示的是类之间的is a关系;组合表示的是类之间的has a关系。
组合关系也可以实现代码的复用,组合类的内部包含了其他类,直接调用其它类对象的属性和方法实现代码复用。
多态
同样的一个方法或行为经过不同的对象表现不同的行为,这样的现象称为多态。
一、方法重写(override)
1、重写的定义
在有继承关系的类之间,子类定义了和父类除了权限不同以外,方法名称、参数列表、返回值类型(向上转换除外)完全相同的方法称为子类重写了父类的方法,子类重写方法的权限>=父类方法。
方法重载(overload):在同一个类中定义了一个方法名称相同,参数列表不同,与返回值无关的一组方法。
区别点 | 重写 | 重载 |
参数列表 | 一定不能修改 | 必须修改 |
返回类型 | 一定不能修改(除非可以构成父子类关系) | 可以修改 |
访问修饰符 | 一定不能做更严格的限制(可以降低限制) | 可以修改 |
方法重写不包括私有方法的重写。父类的私有方法是不被子类所知道的所以不能被重写,
static方法也不存在重写,重写中有不同的对象,而static不存在对象,所以无法重写。
构造方法也不能被重写。构造方法决定父类对象如何产生,子类不能决定父类是如何产生的,所以不能重写。
静态绑定-方法承载:编译时,编译器根据用户传参的不同决定到底调用哪个方法。
动态绑定-方法重写:编译时无法确定到底调用的是哪个对象的方法,在运行时才明确调用哪个方法。
2、实现多态的条件
要想实现多态必须满足以下三个条件:
1、多态的实现必须依赖继承,在继承体系下才有多态。只有在继承关系的类之间才有多态可言。
2、子类必须要复写父类中的方法。
3、通过父类的引用调用子类中重写的方法。
public class Test {public static void main(String[] args) {Cat cat = new Cat("洋洋",3);Dog dog = new Dog("佳佳",4);fun(dog);fun(cat);}public static void fun(Animal animal){animal.eat();}
}
public class Animal {protected String name;protected int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(this.name+"正在吃");}
}
public class Dog extends Animal {public Dog(String name,int age){super(name,age);}public void eat(){System.out.println(this.name+"正在吃狗粮");}
}
public class Cat extends Animal{public Cat(String name,int age){super(name,age);}public void eat(){System.out.println(this.name+"正在吃猫粮");}
}
如上述代码,Cat和Dog是Animal的子类,在两个子类中复写了父类中的eat方法,在主函数中建立了新的Cat和Dog对象cat和dog,当他们都调用相同fun函数时,调用的都是eat方法,传入的是不同的对象,输出了不同的eat行为。
3、 向上转型
天然发生的向上转型,也是以后产生对象的主要方式。
语法:父类名称 父类引用 = new 子类实例();//从右向左读。
通过父类引用调用哪些属性和方法看父类中有哪些属性和方法,父类引用决定了能调用什么。至于调用的方法能表现什么行为取决于new的是哪个子类对象。
public class Test {public static void main(String[] args) {Animal animal = new Dog("洋洋",3);animal.eat();animal.sleep();}
}
public class Dog extends Animal {public Dog(String name,int age){super(name,age);}public void eat(){System.out.println(this.name+"正在吃狗粮");}public void burk(){System.out.println(this.name+"正在汪汪叫");}
}
public class Animal {protected String name;protected int age;public Animal(String name, int age) {this.name = name;this.age = age;}public void eat(){System.out.println(this.name+"正在吃");}public void sleep(){System.out.println(this.name+"正在睡觉");}
}
在主方法中创建父类引用调用Dog类实例,则通过animal对象只能调用eat和sleep方法,不能调用burk方法,并且调用的eat方法还是Dog类中的eat行为,但是sleep并没有被Dog类复写,所以掉用的还是Animal类的sleep方法。
向上转型带来的程序上的优势:参数统一化。
假设实现一个fun方法,接受所有的Animal以及其子类对象,调用这些对象的eat方法,由于Animal以及其子类是完全不同的类型不存在向上转型时引用只能指向当前类型的对象,需要定义很多个不同的重载fun方法,有多少个子类就要重载多少个fun方法。
有了向上转型,无论有多少Animal子类,也不管是不是产生新的Animal子类,只要是Animal的子类,同一可以使用向上转型,通过共同的父类引用animal来指向不同的子类对象,fun只需要定义一次,产生新的子类,fun方法丝毫不影响。
4、向下转型
向上转型只能调用所有子类共有的方法(父类中定义了的)子类独有的方法无法被调用。
语法:子类名称 子类引用 = (子类名称)父类引用。
需要强制类型转换,要发生向下转型,必须先发生向上转型(就是父类运用本来就指向的是子类对象,才能还原为当前这个子类引用,若父类引用指向的不是子类对象,无法向下转型,类型转换异常),否则会报错。
在java中可以用instanceof关键字来判断一个父类引用是否指向一个子类实例。
语法:引用名称 instanceof 类,返回布尔值。
在方法重写中如果父类Animal的返回值是Animal,子类返回子类对象则是可以的,反之不行。
二、final修饰符
1、修饰属性
被修饰的变量成为常量,一旦被修饰并且初始化则永久不能修改。并且具体使用前必须赋值。
2、修饰方法
被修饰的方法无法被重写。
3、修饰类
被修饰的类不能有子类。String类也是final类。