面向对象
- 本质:以类的方式组织代码,以对象的形式封装数据
- 三大特性:封装、继承、多态
封装(encapsulation)
封装就是把抽象出的数据【属性】和对数据的操作【方法】封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作【方法】,才能对数据进行操作
好处:
- 隐藏实现细节 方法(连接数据库)<-调用
- 可以对数据进行验证,保护安全合理
封装的实现步骤:
- 将属性进行私有化private (外部不能直接修改属性)
- 提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}
//可用快捷键Alt+Insert直接得出模板
- 提供一个公共的(public)get 方法,用于获取属性的值
public 数据类型 getXxx(){//权限判断,Xxxv某个属性
return xx;
}
//可用快捷键Alt+Insert直接得出模板
高内聚 低耦合的特点
//封装举例:
public class encapsulation {
public static void main(String[] args) {
Student student = new Student();
student.name="tom";
student.setId(12);
System.out.println(student.id+student.name);//输出为12tom
}
//类 private:私有(外部不能使用)
static class Student {
public String name;//名字公开
private int id;
private char sex;
//提供一些public的get.set方法
//get 获得数据
public int getId(){
return this.id;
}
//此后可以在外部使用类的name值
//set 给数据设值
public void setId(int id) {
if(id>0&&id<100)
this.id = id;
else {
System.out.println("ID需要在0到100之间,给你默认id为99");
this.id=99;//如果输入的id不在范围内则默认id设为99,输出则变成99tom
}
}
}
继承(extend)
- extends 拓展:子类是父类的扩展。子类可以继承父类的所有方法
- super:调用父类的构造器,且必须要在子类构造器的第一行
- 提高代码复用性,提高代码的扩展性和维护性
继承的关系图:
继承举例:
//人:父类
public class Person{
pubilc void say(){
System.out.println("说话");
}
private int money=1_000_000;//私有的,不能被继承
pubilc void print(){
System.out.println("Person");
}
}
//学生是人:派生类、子类:学生
public class Student extends Person{
pubilc void print(){
System.out.println("Student");
}
pubilc void test1(){
print();
this.print();//访问该类方法
super.print();//访问父类方法
}
}
//老师是人:派生类、子类:老师
public class Teacher extends Person{
}
//测试函数:
public static void main(String[] args){
Student student =new Student();
student.say();//Student类用了Person类的方法,输出:说话
student.test1();//输出:Student Student Person
}
//ctrl+h 会展现出继承关系
继承的细节
- 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共的方法去访问
package extend_;
public class Base {//父类
public int n1=100;
private int n2=200;
public Base(){//无参构造器
System.out.println("base()...");
}
public void test001(){
System.out.println("text001");
}
private void test002(){//因为test002为私有所以子类不能直接调用
System.out.println("test002");
}
//父类提供一个public的方法
public int getN2(){//父类提供一个public访问n2
return n2;
}
}
public class sub extends Base {//子类
public sub(){//子类的无参构造器
//super();默认使用该语句,调用父类的无参构造器
System.out.println("sub()...");
}
public void sayOk(){//子类的方法
//非私有的属性方法可以在子类直接访问;
System.out.println(n1);
//System.out.println(n2);
//test002();
//报错私有的禁止访问
System.out.println("n2="+getN2());//子类访问父类public方法即可再访问到n2
}
}
- 子类必须调用父类的构造器,完成父类的初始化
//以1.的父子类关系举例,创造主函数
public class extends02 {
public static void main(String[] args) {
sub a= new sub();
//输出结果为
//base()...即先调用了父类的base()无参构造器
//sub()...再调用类子类的sub()无参构造器
}
}
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作
- super注意点:
- super调用父类的构造方法,必须在构造方法的第一个/第一行
- super必须只能出现在子类的方法或者构造方法中。
- super和this不能同时调用构造方法,因为this()也只能放在构造器的第一行
- super和this的区别:
- 代表的对象不同
- this:本身调用者这个对象
- super:代表父类对象的应用
- 前提
- this:没有继承也可以使用
- super:只能在继承条件才能使用
- 构造方法:
- this():本类的构造
- super():父类的构造
- 代表的对象不同
- 如果希望指定去调用父类的某个构造器,就故意显示地调用一下(平时都是默认调用):super(参数列表)
//举例:
//要调用父类无参构造器,则什么都不写或者写super();
//在创建了子类对象new people("mosun",20)的前提下调用举例
//要调用父类Base(String name)构造器
//super("mosun");
//要调用父类Base(String name,int age)构造器
//super("mosun",20);
- Java所有类都是Object类的子类,Object是所有类的祖宗类(基类)
可用ctrl+H 可得出继承关系。
-
父类构造器的调用不限于直接父类,将一直往上追溯到Object类
-
子类最多只能继承一个父类(直接继承)即java单继承机制
-
不能滥用继承,子类和父类必须满足is-a的逻辑关系
- Person is a Music? X
- Cat is Animal? √
多态(polymorphic)
动态编译:类型(可拓展性)
- 大大提高代码的可用性
- 多态是建立在封装和继承基础之上的
- 多态是方法的多态,属性没有多态
- 父类和子类,有联系才可转换,否则类型转换异常(ClassCastException!)
- 存在条件:继承关系,需要重写,父类引用指向子类对象 Father f1=new Son();
给动物喂食举例:创建食物类(子类为骨头和鱼)、主人、动物类(子类为狗和猫),通过多态减少代码冗余,来实现主人给动物喂食食物的过程
public class Animals {//动物类 父类
private String name;
public Animals(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Cat extends Animals{//🐱猫类继承动物类,子类
public Cat(String name) {
super(name);
}
}
public class Dog extends Animals{//🐕狗类继承动物类,子类
public Dog(String name) {
super(name);
}
}
public class Food {//食物类,父类
private String name;
public Food(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Fish extends Food{//食物类的子类
public Fish(String name) {
super(name);
}
}
public class Bone extends Food{//食物类的子类
public Bone(String name) {
super(name);
}
}
//以上只是简单重复的创建对象
public class Master {//主人
private String name;
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// //主人给狗喂食骨头
// public void feed(Dog dog,Bone bone){
// System.out.println("主人"+name+"给"+dog.getName()+"吃"+bone.getName());
// }
// //主人给猫喂食🐟
// public void feed(Cat cat,Fish fish){
// System.out.println("主人"+name+"给"+cat.getName()+"吃"+fish.getName());
// }
//使用多态机制,统一管理主人喂食问题,代替上面代码
//animal的编译类型是Animal,所以可以接受Animal子类的对象(Cat/Dog)
//food的编译类型是Food,可以接受Food的子类的对象(Fish/Bone)
public void feed(Animals animal,Food food){//喂食语句
System.out.println("主人"
+name+"给"+animal.getName()+"吃"+food.getName());
}
}
public class Poly01 {
public static void main(String[] args) {
Master mosun = new Master("mosun");//创建主人mosun
Dog doggie = new Dog("doggie");//创建狗类对象doggie
Bone bone=new Bone("pork bone");//创建骨头类对象pork bone
Cat tom=new Cat("tom");//创建猫类对象tom
Fish king = new Fish("king");//创建鱼类对象king
mosun.feed(doggie,bone);
mosun.feed(doggie,king);
mosun.feed(tom,king);
//输出为
// 主人mosun给doggie吃pork bone
// 主人mosun给doggie吃king
// 主人mosun给tom吃king
}
}
对象的多态(多态的核心)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时=号左边,运行类型看=号的右边
Animal animal=new Animal//animal的编译类型是Animal,运行类型也是Animal
Animal animal=new Dog();//animal的编译类型是Animal,运行类型是Dog
animal=new Cat();//animal的编译类型仍然是Animal,运行类型则变成了Cat
对象的多态举例
public class Animal {
public void cry(){
System.out.println("动物在叫");
}
}
public class Cat extends Animal{
@Override
public void cry(){
System.out.println("miao");
}
}
public class Dog extends Animal{
@Override
public void cry() {
System.out.println("wang");
}
}
public class PolyObject {
public static void main(String[] args) {
Animal animal=new Dog();//编译类型是animal,运行类型是Dog
animal.cry();//因为运行时,执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry;
animal=new Cat();//运行到这行时,运行类型变成了Cat,所以cry变成了Cat的cry;
animal.cry();
//输出为
//wang
//miao
}
}
多态的细节
- 多态的前提:两个对象(类)存在继承关系
- 多态的向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名=new 子类类型();
- 特点
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限eg:子类私有,父类则无法访问)
- 不能调用子类中特有成员(属性+方法),因为编译阶段能调用哪些成员是由编译类型来决定的
- 多态的向下转型
- 语法:子类类型 引用名=(子类类型)父类引用;
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象
- 可以调用子类类型中所有的成员
public class Person{
public void run(){
System.out.println("run");
}
}
//学生是人:派生类、子类:学生
public class Student extends Person{
public void eat(){
System.out.println("eat");
}
}
public static void main(){
//一个对象的实际类型是确定的
new Student();
new Person();
//但是可以指向的引用类型不确定
Student s1=new Student();
Person s2=new Student();//父类型可以指向子类,但是不能调用子类独有的方法
Object s3=new Student();//Object是所有类的顶级父亲,也就是始祖
//s1.run();父类有该方法可以调用
// s2.eat();父类没有该方法,且不能调用子类的eat方法
//((Student) s2).eat();强制类型转换,此时就可以用子类方法
}