java-面向对象三大特性:封装、继承、多态
封装
属性的修饰符
首先有4个访问修饰符:private;protected;public以及默认也就是前面什么都不加。
在企业中,建议一般将属性修饰符定义为private,为什么呢,因为是私有的,只有当前类可以访问。 这样就可以保证私密性,如果用public修饰那么谁都可以进入当前类,那不就可以随便看你的属性,这样多么不安全。以及在使用构造函数new对象时,如果随意给属性赋值那么会产生很严重的问题。
private 修饰属性后,通过getter以及setter来让创建的对象进行属性的取值和赋值
举一个栗子
/**
现在有一个类来描述汽车,有属性:价格、品牌。
*/
/**
* 汽车类
*
* @author Mould
*
*/
public class Car {
private int price ; //价格
/**
* 品牌
*/
private String brand;
public String getBrand() {//输出属性值,一般情况下
if(band == Null){
band = "" ; //这样就避免了输出null,因为非程序员不理解null是啥,我们就用空字符串输出
}else{
return brand;
}
}
public void setBrand(String brand) {
if(band == "mini"){//这个对传递的品牌进行判断。这个就避免了用户随意传值。
band = "宝马" ;
}else{
this.brand = brand;
}
}
//再将main方法写在另外一个同包的java文件中,不建议将main方法写在类中
//这样就可以给对象的属性通过setBrand()给品牌赋值,通过setBrand()取出品牌值,这里只写了品牌这个属性,其他属性是一样
//的,就是在方法名中Brand改为你想赋值以及取值的属性名
public class CarDemo {
public static void main(String[] args) {
Car car = new Car() ;
car.setBrand("mini");
String b = car.getBrand() ;
System.out.println("品牌为:" + b) ;
}
}
封装的作用
- 对属性的访问进行控制
- 一个类对外隐藏实现的细节
最上面的栗子是对属性进行讲解,也就是第一个作用
第二个作用主要是作用于方法。
再举个栗子
我们启动汽车,无论是一键启动,还是用钥匙拧开,汽车都会启动,其实汽车启动的过程可以分解为喷油–活塞花点火–启动引擎。那么我们实际上都没有进行这些操作流程,那么就是系统自己将启动的过程封装成了一个方法叫启动,一旦用户点了启动,那么系统自己偷偷后台运行这些流程,我们用户无法调用喷油这个方法,点火这个方法以及启动这个方法。就是将这些方法隐藏起来了。
上代码
public class Car { /** * 这里面就不写汽车属性了,主要写汽车启动的过程 */ public void start() {//启动方法 //因为该方法是public修饰的所以任何地方都可以访问到 oil(); fireUp(); startEngine(); } /** * 喷油,修饰为private那么只能在本类中被调用 */ private void oil() { System.out.println("汽车开始喷油"); } /** * 点火,修饰为private那么只能在本类中被调用 */ private void fireUp() { System.out.println("汽车开始点火"); } /** * 启动发动机,修饰为private那么只能在本类中被调用 */ private void startEngine() { System.out.println("汽车发动机启动"); } }
再写一个.java文件进行创建对象调用启动方法
public class CarDemo {
public static void main(String[] args) {
// 构造汽车类的对象
Car car1 = new Car() ;
car1.start();//汽车启动
//car1.oil(); //这个会报错,因为private修饰不能再其他类中访问到。
}
}
继承
java只有单继承,也就是一个子类只能继承一个父类,换言之,只有一个爸爸。但是可以你继承你爸爸的,你爸爸继承你爷爷的,你爷爷继承你太爷爷的,一直这样继承下去,那么你就暗地里继承了你太爷爷的,你爷爷的。这样说人话,好理解,好记忆。
子类自动有了父类的属性和方法。这样就减少代码的冗余,便于维护和扩展。子类可以写自己的属性和方法。
继承使用extends关键字
举个栗子:如何继承减少代码冗余度。
上代码
//动物类
public class Animal {
/**
* 公有属性:年龄
*/
protected String brand ;
protected int age ;
protected String sex ;
protected void eat() {
System.out.print("调用动物类的吃方法");
}
protected void dirnk() {
System.out.print("调用动物类的喝方法");
}
}
//同包文件下创建一个Dog类继承Animal
public class Dog extends Animal{
protected String furColor ; //狗独有的属性:毛色,动物类里面没有就要单独写
protected String size ; //大小
}
//同包下创建YanZi类
public class YanZi extends Animal {
//这里面啥都不用写,因为继承动物类就行了,这不用写代码,不香吗。
}
有啥不能继承的
- 父类的构造函数能继承吗,(答案是不能,因为构造方法和类名同名,父类名和子类名怎么相同呢)
- 父类的私有的属性和方法,他们能继承吗,(答案是不能,因为构造方法和类名同名,父类名和子类名怎么相同呢)
访问修饰符
方法的重写
重写(overload)是子类继承父类时,父类写了某个方法,但是子类想用时,发现部分不是自己想要的或者是完全都不是自己想要的,那么就写一个和父类这个方法相同的方法名。那么在子类中调用该方法自动调用子类重写的方法。
举个栗子
//这里就用上面的动物来写吧,只是简单的栗子,证明上面的栗子
//修改Dog.java文件
public class Dog extends Animal{
protected String furColor ; //狗独有的属性:毛色,动物类里面没有就要单独写
protected String size ; //大小
@Override//解释器作用,表示下面的方法是重写
public void eat() {
System.out.println("狗喜欢吃的到处都是");
}
}
//同包下创建Demo.java
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog() ;
dog.eat();
}
}
//运行这个程序
看结果
可以使用@Override注解定义在方法上,用来检查该方法是否构成方法的重写。
栗子就在上面的代码那里
super关键字的用法
- super就是代指父类的对象
- super()就是调用父类的无参构造方法,和this()很像。
- super.属性和super.方法名()调用父类的属性和方法。这个一般使用在子类重写了父类方法,因为重写了父类方法,那么父类的该方法就不能被调用了
- super()必须写在方法中第一行,父类嘛,答谢当然要先写它。
看个栗子,子类重写了父类的方法,采用super.方法名()调用父类的方法,
public class Animal {
/**
* 公有属性:年龄
*/
protected String brand ;
protected int age ;
protected String sex ;
protected void eat() {
System.out.print("调用动物类的吃方法");
}
protected void dirnk() {
System.out.print("调用动物类的喝方法");
}
}
public class Dog extends Animal{
protected String furColor ; //狗独有的属性:毛色,动物类里面没有就要单独写
protected String size ; //大小
@Override//解释器作用,表示下面的方法是重写
public void eat() {
super.eat(); //调用父类的eat()方法
System.out.println("狗喜欢吃的到处都是");
//super.eat(); //这样会报错,super()必须放在第一行
}
}
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog() ;
dog.eat();
}
}
举个栗子,super调用父类的构造方法
//父类
public class Animal {
/**
* 公有属性:年龄
*/
protected String brand ;
protected int age ;
protected String sex ;
public Animal(int age , String sex) {
this.age = age ;
this.sex = sex ;
}
public Animal(String brand ,int age , String sex) {
this(age,sex); //直接采用this(参数)来调用其他的构造方法
this.brand = brand;
//this(age,sex); //直接采用this(参数)来调用其他的构造方法
}
protected void eat() {
System.out.println("调用动物类的吃方法");
}
protected void dirnk() {
System.out.println("调用动物类的喝方法");
}
}
//子类-Dog
public class Dog extends Animal{
protected String furColor ; //狗独有的属性:毛色,动物类里面没有就要单独写
protected String size ; //大小
public Dog(String brand ,int age , String sex , String furColor , String size ) {//因为父类里面包含-有参构造方法,那么子类必须定义个构造函数,
//因为默认情况系统写个无参的构造函数自己调用了,但是你在类里面写了有参构造方法,那么就会调用你写的有参的构造函数
super(brand , age , sex);//super()必须放在第一行,调用父类的有三个参数的构造方法
this.furColor = furColor ; //这个是子类的构造函数传值
this.size = size ; //这个是子类的构造函数传值
}
@Override//解释器作用,表示下面的方法是重写
public void eat() {
super.eat(); //调用父类的eat()方法
System.out.println("狗喜欢吃的到处都是");
//super.eat(); //这样会报错,super()必须放在第一行
}
}
//子类-燕子
public class YanZi extends Animal {
public YanZi(int age, String sex) {
super(age, sex);
// TODO Auto-generated constructor stub
}
}
public class Demo {
public static void main(String[] args) {
Dog dog = new Dog("哈士奇" , 3 , "男" , "黑白色" , "中性犬") ;//子类的构造方法
dog.eat();//子类的eat()方法
}
}
多态
使用父类的引用去指向子类的对象。
此时只能调用父类里面有的属性和方法。如果子类中有重写父类的方法,那么会调用子类的重写后的方法。
Father father = new Son();
//等号左边,用父类的变量
//等号右边,new 子类的对象
然后father这个对象名只能调用父类里面的方法。
如果子类里面有重写父类里面的方法,那么father.那个方法名(),实际上调用的是子类里面重写的方法。调用其他没有重写的方法,就调用父类里面的方法。
使用场景:
方法参数的多态
方法返回值的多态
现在父类是动物类,有方法:吃和睡。
一个子类:狗,有方法:吃,睡,狗叫,摇尾巴。
另一个子类:猫,有方法:吃,喝,猫叫。
有个主人类:有方法喂食物。
上代码。
public class Animal {
public void shock() {
System.out.println("动物在叫");
}
public void eat() {
System.out.println("动物在吃");
}
}
public class Dog extends Animal{
@Override
public void shock() {
System.out.println("狗在汪汪叫");
}
@Override
public void eat() {
System.out.println("狗在吃");
}
public void sleep() {
System.out.println("狗在睡");
}
}
public class Cat extends Animal{
@Override
public void shock() {
System.out.println("猫在叫");
}
@Override
public void eat() {
System.out.println("猫在吃");
}
}
public class Master {
public void feed( Animal a) {
a.eat();
}
public Animal play() {
//返回值是一个类
//这也是一个多态
return new Cat() ;
}
}
public class Demo {
public static void main(String [] args) {
/**
* 父类的引用指向子类的对象,只能调用父类里面有的方法,
* 如果该方法被子类重写那么实际调用子类的方法
*/
Animal a = new Dog() ;
a.eat();
a.shock();
Dog dog = new Dog() ;
Master m = new Master() ;
m.feed(a);//参数为父类类型,但是开辟的是子类的对象,就调用子类重写的方法。
m.feed(dog);//这里是将创建子类对象,传到父类的类型,这样就实现了多态的可扩展性。
}