Java基础之面向对象
面向对象的三大特性:封装性、继承性、多态性
1.方法就是一种封装
2.关键字private也是一种封装
封装就是将一些细节信息隐藏起来,对于外界不可见
package com.dcits.day05.demo03; public class Demo02Method { public static void main(String[] args) { int[] array = {5,15,25,35,111}; int max = getMax(array); System.out.println(max); } public static int getMax(int[] array) { int max = array[0]; for (int i = 0; i < array.length; i++) { if (array[i] > max) { max = array[i]; } } return max; } }
private关键字的作用以及使用
一旦使用了private进行修饰,那么本类中可以随意访问,但是超出了本类的范围就不能再访问了
可以通过间接访问的方式,自定义一对儿Getter/Setter方法,必须叫 setXxx 或者是 getXxx
对于Getter来说,不能有参数,返回值类型和成员变量对应
对于Setter来说,不能有返回值,参数类型和成员变量对应
// Person类 package com.dcits.day05.demo03; public class Person { String name; private int age; public void show() { System.out.println("我叫:" + name +",今年" + age); } public void setAge(int num) { if (num < 100 && num >=9) { age = num; } else { age = 0; System.out.println("数据不合理"); } } public int getAge() { return age; } } // 调用 package com.dcits.day05.demo03; public class Demo03Person { public static void main(String[] args) { Person person = new Person(); person.name = "赵丽颖"; // person.age = 18; 当成员变量被private修饰的时候,外部无法访问,只能通过间接的方式Setter,Getter person.setAge(-20); person.show(); } }
布尔类型的特殊情况
// 类 package com.dcits.day05.demo03; public class Student { private String name; private int age; private boolean male; public void setMale(boolean b){ male = b; } public boolean isMale() { return male; } public void setName(String str){ name = str; } public String getName() { return name; } public void setAge(int num) { age = num; } public int getAge() { return age; } } // 调用 package com.dcits.day05.demo03; public class Demo04Student { public static void main(String[] args) { Student stu = new Student(); stu.setName("alex"); stu.setAge(10); stu.setMale(true); System.out.println(stu.getName()); System.out.println(stu.getAge()); System.out.println(stu.isMale()); } }
this关键字的使用
当方法的局部变量和类的成员变量重名的时候,根据就近原则,优先使用局部变量,如果需要访问 本类当中的成员变量,需要使用格式:this.成员变量
通过谁调用的方法,谁就是this
// 类 package com.dcits.day05.demo04; public class Person { String name; public void sayHi(String name) { System.out.println(this.name + "你好,我是" + name); } } // 调用 package com.dcits.day05.demo04; public class Demo01Person { public static void main(String[] args) { Person person = new Person(); person.name = "6666"; person.sayHi("777"); } }
构造方法
构造方法是专门用来创建对象的方法,当我们使用关键字new来创建对象的时候,其实就是在调用构造方法
注意事项:
-
构造方法的名称必须和所在的类名称完全一样,就连大小写也要完全一样
-
构造方法不要写返回值类型,连void都不要写
-
构造方法不能return一个具体的返回值
-
如果没有编写任何构造方法,那么编译器默认会赠送一个构造方法,没有参数、方法体什么都不做
-
一旦编写了一个构造方法,那么编译器就不再赠送
-
构造 方法也是可以重载的
// 类 package com.dcits.day05.demo04; public class Student { private String name; private int age; public Student(String name,int age) { this.name = name; this.age = age; System.out.println("有参数的构造方法!!"); } public Student() { System.out.println("无参数的构造方法执行啦!!"); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } } // 调用 package com.dcits.day05.demo04; public class Demo02Student { public static void main(String[] args) { Student stu = new Student(); Student stu1 = new Student("aaa",20); stu1.setAge(23); System.out.println(stu1.getAge()); System.out.println(stu1.getName()); } }
局部变量和成员变量
-
定义的位置不一样
-
局部变量:在方法的内部
-
成员变量:在方法的外部,直接写在类当中
-
-
作用范围不一样
-
局部变量:只有方法 才可以使用,出了方法就不能再使用了
-
成员变量:整个类都可以通用
-
-
默认值不一样
-
局部变量:没有默认值,如果想要用,必须手动赋值
-
成员变量:如果没有赋值,会有默认值,规则和数组一样
-
-
内存的位置不一样
-
局部变量:位于栈内存
-
成员变量:位于堆内存
-
-
生命周期不一样
-
局部变量:随着方法进栈而诞生,随着方法出栈而消失
-
成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
-
标准类的组成部分
一个标准的类通常拥有下面的四个部分:
-
所有的成员变量都要使用private关键字来修饰
-
为每一个成员变量编写一对Getter、Setter方法
-
编写一个无参数的构造方法
-
编写一个全参数的构造方法
继承
继承是多态的前提,如果没有集成,就没有多态
继承解决的主要问题就是:共性抽取
定义类时的两个注意事项:
-
成员变量时直接定义在类当中的,在方法外面
-
成员方法不要写static关键字
继承的格式:
// 父类 package com.dcits.day08.demo01; public class Employee { public void method() { System.out.println("父类执行!"); } } // Teacher子类 package com.dcits.day08.demo01 public class Teacher extends Employee{ } // Assistant子类 package com.dcits.day08.demo01; public class Assistant extends Employee { } // 调用 package com.dcits.day08.demo01; public class Demo01Extends { public static void main(String[] args) { Teacher teacher = new Teacher(); Assistant assistant = new Assistant(); teacher.method(); assistant.method(); } }
在父子类的继承关系当中,如果成员变量重名,则创建子类时,访问有两种方式:
-
直接通过子类对象访问成员变量
-
等号左边是谁,就优先使用谁,没有则向上找
-
-
间接通过成员方法访问成员变量
-
该方法属于谁,就优先用谁,没有则向上找
-
// 父类 package com.dcits.day08.demo02; public class Fu { int numFu = 10; int num = 100; public void methodFu() { System.out.println(num); } } // 子类 package com.dcits.day08.demo02; public class Zi extends Fu { int numZi = 20; int num = 200; public void methodZi() { System.out.println(num); } } // 调用 package com.dcits.day08.demo02; public class Demo01ExtendsField { public static void main(String[] args) { Fu fu = new Fu(); System.out.println(fu.numFu); Zi zi = new Zi(); System.out.println(zi.numFu); System.out.println(zi.numZi); // 当父类与子类的成员变量重名的时候 System.out.println(zi.num); // System.out.println(zi.abc); // zi.methodZi(); zi.methodFu(); } }
区分子类方法中的三种重名变量
-
直接使用的方法中的变量
-
this.变量名:调用本类中的成员变量
-
super.变量名:调用父类中的成员变量
// 父类 package com.dcits.day08.demo03; public class Fu { int num = 10; } // 子类 package com.dcits.day08.demo03; public class Zi extends Fu { int num = 20; public void method() { int num = 30; System.out.println(num); // 30 局部变量 System.out.println(this.num); // 20 本类的成员变量 System.out.println(super.num); // 10 父类的成员变量 } } // 调用 package com.dcits.day08.demo03; public class Demo01ExtendsField { public static void main(String[] args) { Zi zi = new Zi(); zi.method(); } }
继承中成员方法的访问特点
在父子类的继承关系当中,创建子类对象,访问成员方法的规则:创建的对象是谁,就优先使用谁,如果没有则向上找
注意:无论是成员方法还是成员变量,如果没有都是向上找父类u,绝不会向下找子类
// 父类 package com.dcits.day08.demo04; public class Fu { public void methodFu() { System.out.println("父类中的方法执行啦!"); } public void method() { System.out.println("父类重名执行啦!"); } } // 子类 package com.dcits.day08.demo04; public class Zi extends Fu { public void methodZi() { System.out.println("子类中的方法执行啦!"); } public void method() { System.out.println("子类重名执行啦!"); } } // 调用 package com.dcits.day08.demo04; public class Zi extends Fu { public void methodZi() { System.out.println("子类中的方法执行啦!"); } public void method() { System.out.println("子类重名执行啦!"); } }
继承方法中的覆盖重写
重写:方法的名称一样,参数列表也一样,覆盖、覆写
重载:方法的名称一样,参数列表不一样
方法的覆盖重写特点:创建的是子类对象,则优先用子类方法
方法覆盖重写的注意事项:
-
必须保证父子类之间的方法名称相同、参数列表也相同
-
@Override:写在方法前面,用来检测是不是有效的正确覆盖重写,这个注释就算不写,只要满足要求,也是正确的覆盖重写
-
子类方法的返回值必须小于等于父类方法的返回值范围。Object类是所有类的父类
-
子类方法的权限必须大于等于父类方法的权限修饰符。public > protected > (default) > private 注意:(default)不是关键字default,而是什么都不写,留空
继承中方法的覆盖重写应用场景
// 父类 package com.dcits.day08.demo06; // 本来的老款手机 public class Phone { public void call() { System.out.println("打电话"); } public void send() { System.out.println("发短信"); } public void show() { System.out.println("显示号码"); } } // 子类 package com.dcits.day08.demo06; // 上市的新款手机 public class NewPhone extends Phone { @Override public void show() { // System.out.println("显示号码"); super.show(); System.out.println("显示姓名"); System.out.println("显示头像"); } } // 调用 package com.dcits.day08.demo06; public class Demo01Phone { public static void main(String[] args) { Phone phone = new Phone(); phone.call(); phone.send(); phone.show(); System.out.println("==========="); NewPhone newPhone = new NewPhone(); newPhone.call(); newPhone.send(); newPhone.show(); } }
继承中构造方法的访问特点
-
子类构造方法当中有一个默认隐含的 "super()" 调用,所以一定是先调用的父类构造,后执行的子类构造
-
子类构造可以通过super关键字来调用父类重载构造
-
super的父类构造调用,只能是第一个语句 ,不能一个子类构造调用多次super构造
-
子类必须调用父类构造方法,不写赠送super() 写了则用写的指定该的super调用,super只能有一个,还必须是第一个
super关键字的用法(访问父类的内容):
-
在子类的成员方法中,访问父类的成员变量
-
在子类的成员方法中,访问父类的成员方法
-
在子类的构造方法中,访问父类的构造方法
// 父类 package com.dcits.day08.demo08; public class Fu { int num = 10; public void method(){ System.out.println("父类方法"); } } // 子类 package com.dcits.day08.demo08; public class Zi extends Fu { int num = 20; public Zi(){ super(); } public void methodZi() { System.out.println(super.num); // 父类的num } public void method(){ super.method(); System.out.println("子类方法"); } }
this关键字的三种使用方法(访问本类的内容)
-
在本类的成员方法中,访问本类的成员变量
-
在本类的成员方法中访问本类的另一个成员方法
-
在本类的构造方法中,访问本类的另一个构造方法
注意:
-
在第三种用法中要注意:this(..)调用必须是构造方法的一个语句,唯一一个
-
super和this两种构造调用,不能同时使用
// 父类 package com.dcits.day08.demo09; public class Fu { int num = 30; } // 子类 package com.dcits.day08.demo09; public class Zi extends Fu { int num = 20; public Zi(){ this(123); // 本类的无参构造,调用本类的有参构造 // this(1,2) } public Zi(int n){ } public Zi(int n,int m){ } public void showNum(){ int num = 10; System.out.println(num); System.out.println(this.num); // 本类中的成员变量 System.out.println(super.num); // 父类中的 成员变量 } public void methodA() { System.out.println("AAA"); } public void methodB() { methodA(); this.methodA(); System.out.println("BBB"); } }
this、super的关键字图解
Java语言继承的三个特点:
-
一个类的 直接父类只能有唯一一个
-
Java语言可以多继承
-
一个子类的直接父类是唯一的,但是一个父类可以拥有很多个子类
多态
多态的定义以及基本使用
extends继承或者implements实现,是多态的前提。
小明这个对象既有学生形态,也有人类形态。一个对象拥有多种形态,这就是:对象的多态性
代码当中体现多态性,其实就是一句话:父类引用指向子类对象
格式:
-
父类名称 对象名 = new 子类名称()
-
接口名称 对象名 = new 实现类名称()
// 父类 package com.dcits.day09.demo04; public class Fu { public void method(){ System.out.println("父类方法"); } public void methodFu(){ System.out.println("父类特有方法"); } } // 子类 package com.dcits.day09.demo04; public class Zi extends Fu { @Override public void method() { System.out.println("子类方法"); } } // 调用 package com.dcits.day09.demo04; public class Demo01Multi { public static void main(String[] args) { // 使用多态的写法 // 左侧父类的引用指向右侧子类的对象 Fu obj = new Zi(); // new 的是谁就调用谁 的方法 obj.method(); // 子类方法 obj.methodFu(); // 父类特有方法 } }
多态中成员变量的使用特点
访问成员变量的两种方式:
-
直接通过对象名称访问成员变量,看等号左边是谁,优先用谁,没有则向上找
-
间接通过成员方法访问成员变量,看该方法属于谁,优先用谁,没有则像上找
// 父类 package com.dcits.day09.demo05; public class Fu { int num = 10; public void showNum(){ System.out.println(num); } } // 子类 package com.dcits.day09.demo05; public class Zi extends Fu { int num = 20; int age = 16; @Override public void showNum() { System.out.println(num); } } // 调用 package com.dcits.day09.demo05; public class Demo01MultiField { public static void main(String[] args) { Fu obj = new Zi(); System.out.println(obj.num); // 父类中的10 System.out.println("====================="); obj.showNum(); // 子类没有覆盖重写,就是父类中的num,一旦子类重写后就是子类中的num } }
多态中成员方法的使用特点
在多态的代码当中,成员方法的优先访问规则是:看new的是谁,就优先用谁,没有则向上找
注意:编译看左边,运行看右边
对比一下:
-
成员变量:编译看左边,运行还看左边
-
成员方法:编译看左边,运行看右边
// 父类 package com.dcits.day09.demo05; public class Fu { int num = 10; public void showNum(){ System.out.println(num); } public void method(){ System.out.println("父类方法"); } public void methodFu(){ System.out.println("父类特有方法"); } } // 子类 package com.dcits.day09.demo05; public class Zi extends Fu { int num = 20; int age = 16; @Override public void showNum() { System.out.println(num); } @Override public void method() { System.out.println("子类方法"); } public void methodZi(){ System.out.println("子类特有方法"); } } // 调用 package com.dcits.day09.demo05; public class Demo01MultiField { public static void main(String[] args) { Fu obj = new Zi(); obj.method(); // 父子都有,优先使用子类 obj.methodFu(); // 子类没有,父类有,向上找到父类 // 编译看左,左边是Fu,没有methodZi方法,所以编译报错 // obj.methodZi(); // 错误写法 // System.out.println(obj.num); // 父类中的10 // System.out.println("====================="); // obj.showNum(); // 子类没有覆盖重写,就是父类中的num,一旦子类重写后就是子类中的num } }
使用多态的好处
对象的向上转型
对象的向上转型,其实就是多态写法
格式: 父类名称 对象名 = new 子类名称()
含义:右侧创建一个子类对象,把它当作父类来看待使用
注意事项:
-
向上转型一定是安全的,从小范围转到了大范围
-
但是有一个弊端:对象一旦向上转型为父类,那么就无法调用子类原本的特有内容
类似于:double num = 100 正确 int----double 自动类型转换
// 父类 package com.dcits.day09.demo06; public abstract class Animal { public abstract void eat(); } // 子类 package com.dcits.day09.demo06; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼。。。"); } } // 调用 package com.dcits.day09.demo06; public class Demo01Main { public static void main(String[] args) { Animal animal = new Cat(); animal.eat(); } }
对象的向下转型
对象的向下转型,其实是一个还原动作
格式:子类名称 对象名 = (子类名称) 父类对象
含义:将父类对象,还原成为原本的子类对象
注意事项:
-
必须保证对象本来创建的时候,就是猫,才能向下转型成为猫
-
如果对象创建的时候本来不是猫,现在非要向下转型成为猫,就会报错
类似于:int num = (int) 10.0 正确 int num = (int) 10.5 错误,发生精度损失
// 父类 package com.dcits.day09.demo06; public abstract class Animal { public abstract void eat(); } // 猫子类 package com.dcits.day09.demo06; public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼。。。"); } public void catchMouse() { System.out.println("猫抓老鼠!!"); } } // 狗子类 package com.dcits.day09.demo06; public class Dog extends Animal { @Override public void eat() { System.out.println("狗吃shit"); } public void watchMouse() { System.out.println("狗看家!!"); } } // 调用 package com.dcits.day09.demo06; public class Demo01Main { public static void main(String[] args) { Animal animal = new Cat(); // 对象的向上转型 animal.eat(); // 向下转型 Cat cat = (Cat) animal; cat.catchMouse(); // 猫抓老鼠!! // 下面是错误的向下转型 // 本来new的时候是一只猫,现在非要转成狗 // java.lang.ClassCastException Dog dog = (Dog) animal; // 错误写法 } }
用instanceof 关键字进行类型判断
package com.dcits.day09.demo06; public class Demo01Instanceof { public static void main(String[] args) { Animal animal = new Cat(); // 本来是一只猫 animal.eat(); // 如果需要调用子类特有的方法,需要向下转型 if (animal instanceof Dog){ Dog dog = (Dog) animal; dog.watchMouse(); } if (animal instanceof Cat){ Cat cat = (Cat) animal; cat.catchMouse(); } giveMeAPet(new Dog()); // 在你调用这个方法的时候,方法本身不知道传递过来的是哪个类,所以需要进行判断 } public static void giveMeAPet(Animal animal){ if (animal instanceof Dog){ Dog dog = (Dog) animal; dog.watchMouse(); } if (animal instanceof Cat){ Cat cat = (Cat) animal; cat.catchMouse(); } } }
接口多态的综合案例
// USB接口类:两个抽象方法:打开USB、关闭USB package com.dcits.day09.demo07; public interface USB { public abstract void open(); public abstract void close(); } // 电脑类:开机、关机、连接USB接口并对USB设备进行对应操作 package com.dcits.day09.demo07; public class Computer { public void powerOn(){ System.out.println("笔记本电脑开机"); } public void powerOff(){ System.out.println("笔记本电脑关机"); } // 使用USB设备的方法,使用接口作为方法的参数 public void useDevice(USB usb) { usb.open(); // 判断当前类是属于哪个类之后,在获取类中的特有方法 if (usb instanceof Mouse){ Mouse mouse = (Mouse) usb; mouse.click(); } else if (usb instanceof KeyBoard){ KeyBoard keyboard = (KeyBoard) usb; keyboard.type(); } usb.close(); } } // 鼠标类:重写接口类中的打开、关闭功能,并实现自己的独有功能 package com.dcits.day09.demo07; public class Mouse implements USB { @Override public void open() { System.out.println("打开鼠标"); } @Override public void close() { System.out.println("关闭鼠标"); } public void click(){ System.out.println("点击鼠标!"); } } // 键盘类:重写接口类中的打开、关闭功能,并实现自己的独有功能 package com.dcits.day09.demo07; public class KeyBoard implements USB { @Override public void open() { System.out.println("打开键盘"); } @Override public void close() { System.out.println("关闭键盘"); } public void type(){ System.out.println("敲键盘!"); } }