面向对象的三大特征

面向对象的三大特征:继承、封装、多态

封装

封装是面向对象编程的核心思想,将对象的属性和行为封装隐藏起来。而这些封装起来的属性和行为通常对外隐藏细节,只允许外部使用公开的数据,避免外部操作导致内部数据改变,来保证类内部数据和结果的完整性,提高程序的可维护性。

为什么要封装

在Java面向对象的思想中,封装可以被认为是一个保护屏障,防止本类的代码和数据被外部程序随机访问。下面通过一个例子具体讲解什么是封装

class Student {
	String name;
	int age;
	void read() {
		System.out.println("我是"+name+",今年"+age);
	}
}
public class Test{
	public static void main(String[] args) {
			Student s=new Student();    
			s.setName("张三");
			s.setAge(-18);
			s.read();
		}
	}

第10行代码将年龄赋值为-18岁,这在程序中是不会有任何问题的,因为int的值可以取负数。但是在现实中,-18明显是一个不合理的年龄值。为了避免这种错误的发生,在设计Student类时,应该对成员变量的访问做出一些限定,不允许外界随意访问,这就需要实现类的封装。

 如何实现封装

类的封装是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类提供的方法实现对内部信息的操作访问。

在Java开发中,在定义一个类时,将类中的属性私有化,即使用private关键字修饰类的属性,被私有化的属性只能在类中被访问。如果外界想要访问私有属性,则必须通过setter和getter方法设置和获取属性值。

下面修改例子,使用private关键字修饰name属性和age属性,实现类的封装。

class Student {
	private String name;
	private int age;

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		if (age <= 0) {
			System.out.println("您输入的年龄有误!");
		} else {
			this.age = age;
		}
	}

	void read() {
		System.out.println("我是" + name + ",今年" + age);
	}

}
public class Test{
	public static void main(String[] args) {
			Student s=new Student();    
			s.setName("张三");
			s.setAge(-18);
			s.read();
		}
	}

使用private关键字将属性name 和age声明为私有变量,并对外界提供公有的访问方法,其中,getName()方法和getAge()方法用于获取name属性和age属性的值,setName()方法和setAge()方法用于设置name属性和age属性的值。

运行结果:

您输入的年龄有误!
我是张三,今年0

当调用setAge()方法传入了-18这个负数后, age显示为初始值0。这是因为setAge ()方法对参数age进行了判断,如果age的值小于或等于0,会打印“您输入的年龄有误!”,并将age设置为0。

类的继承

在现实生活中,继承一般是指子女继承父辈的财产。在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。例如,猫和狗都属于动物,程序中便可以描述为猫和狗继承自动物,同理,波斯猫和巴厘猫继承猫科,而沙皮狗和斑点狗继承自犬科。这些动物之间会形成一个继承体系。

在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类称为子类,现有类称为父类。子类继承父类的属性和方法,使得子类对象(实例)具有父类的特征和行为。在程序中,如果想声明一个类继承另一个类,需要使用extends关键字,语法格式如下。

class 父类{
......
}
class 子类extenda父类{
......
}

从上述语法格式可以看出,子类需要使用extends关键字实现对父类的继承。下面通过一个案例类是如何继承父类的。

class Animal {
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
}
class Dog extends Animal{
	
}
public class Test{
	public static void main(String[] args) {
			Dog dog=new Dog();
			dog.setName("旺旺");
			dog.setAge(1);
			System.out.println("我是"+dog.getName()+",今年"+dog.getAge()+"岁");
		}
	}

运行结果:

我是旺旺,今年1岁

Dog类中并没有定义任何操作,而是通过extends关键字继承了Animal类,成为Animal类的子类。子类虽然没有定义任何属性和方法,但是能调用父类的方法。这就说明子类在继承父类的时候,会自动继承父类的成员。除了继承父类的属性和方法,子类也可以定义自己的属性和方法.

class Dog extends Animal{
	private String color;

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
	
}
public class Test{
	public static void main(String[] args) {
			Dog dog=new Dog();
			dog.setName("旺旺");
			dog.setAge(1);
			dog.setColor("黑");
			System.out.println("我是"+dog.getName()+",今年"+dog.getAge()+"岁,"+dog.getColor()+"色");
		}
	}

在类的继承中,需要注意一些问题

1.在Java中,类只支持单继承,不允许多重继承。也就是说,一个类只能有一个直接父类,例如下面这种情况是不合法的。

class A{}
class B{}
class C extends A,B{}   //C类不可以同时继承A类和B类

2.多个类可以继承一个父类,例如下面这种情况是允许的。

class A{}
class B extends A{}
class C extends A{}   //B类和C类都可以继承A类

3.在Java中,多层继承也是可以的,即一个类的父类可以再继承另外的父类。例如,C类继承自B 类,而B类又可以继承自A类,这时,C类也可称为A类的子类。例如,下面这种情况是允许的。

class B extends A{}   //B类继承A类,B类是A类的子类
class C extends B{}   //C类继承B类,C类是B类的子类,同时也是A类的子类

在继承中,子类不能直接访问父类中的私有成员,子类可以调用父类的非私有方法,但是不能调用父类的私有成员。

方法的重写 

在继承关系中,子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表和返回的类型,且在子类重写的方法不能拥有比父类方法更加严格的访问权限。

class Animal {
	void shout() {
		System.out.println("动物再叫");
	}
}
class Dog extends Animal{
	void shout() {
		System.out.println("狗再叫,汪汪汪");
	}
}
public class Test{
	public static void main(String[] args) {
			Dog dog=new Dog();
			dog.shout();
		}
	}

运行结果:

狗再叫,汪汪汪

dog对象调用的是子类重写的shout()方法,而不是父类的shout()方法。

子类重写父类方法时,不能使用比父类中被重写方法更严格的访问权限。例如,父类中的方法是public权限,子类的方法就不能是private权限。如果子类在重写父类方法时定义的权限缩小,则在编译时将出现错误提示。

super关键字 

当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,为了解决这个问题, Java提供了super 关键字, super关键字可以在子类中调用父类的普通属性、方法和构造方法。

(1)使用super关键字访问父类的成员变量和成员方法

super.成员变量
super.成员方法(参数1,参数2…)
class Animal {
	String name="中华田园犬";
	void shout() {
		System.out.println("动物再叫");
	}
}
class Dog extends Animal{
	void shout() {
		super.shout();  	//调用父类中的shout()方法
		System.out.println("狗再叫,汪汪汪");
	}
	 public void printName()  {
		   System. out. println("名字:"+super.name);   //调用父类中的name属性                       //调用父类中的name属性
	}
}
public class Test{
	public static void main(String[] args) {
			Dog dog=new Dog();
			dog.shout();
			dog.printName();
		}
	}

 运行结果:

动物再叫
狗再叫,汪汪汪
名字:中华田园犬

在Dog类的shout()方法中使用“super. shout()”调用了父类被重写的shout()方法。在printName()方法中使用“super. name”访问父类的成员变量name。子类通过super关键字可以成功地访问父类成员变量和成员方法。

(2)使用super关键字访问父类中指定的构造方法,具体格式如下:

super(参数1,参数2…)
class Animal {
	private String name;
	private int age;

	public Animal(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String info() {
		return "名称:" + this.getName() + ",年龄:" + this.getAge();
	}
}

// 定义Dog类继承Animal类
class Dog extends Animal {
	private String color;

	public Dog(String name, int age, String color) {
		super(name, age);
		this.setColor(color);
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	// 重写父类的info()方法
	public String info() {
		return super.info() + ",颜色:" + this.getColor();// 扩充父类中的方法
	}
}

public class Test {
	public static void main(String[] args) {
		Dog dog = new Dog("旺旺",3,"黑色");
		System. out. println(dog. info());
	}
}

运行结果:

名称:旺旺,年龄:3,颜色:黑色

使用super()调用了父类中有两个参数的构造方法,子类Dog中重写了父类Animal中的info()方法。在子类中重写了父类的info()方法,使用子类的实例化对象调用info()方法时,会调用子类中的info()方法。

通过super()调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。super与this关键字的作用非常相似,都可以调用构造方法、普通方法和属性,但是两者之间还是有区割的, super与this的区别如表所示。

 

  this和super两者不可以同时出现,因为this和super在调用构造方法时都要求必须放在构造方法的首行。

多态

多态性是面向对象思想中的一个非常重要的概念,在Java中,多态是指不同对象在调用同一个方法时表现出的多种不同行为。例如,要实现一个动物叫的方法,由于每种动物的叫声是不同的,因此可以在方法中接收一个动物类型的参数,当传入猫类对象时就发出猫类的叫声,传人犬类对象时就发出犬类的叫声。在同一个方法中,这种由于参数类型不同而导致执行效果不同的现象就是多态。Java中多态主要有以下两种形式。

(1)方法的重载。

(2)对象的多态性(方法重写)。

abstract class Animal {
	abstract void shout(); // 定义抽象shout()方法
}

// 定义Cat类继承Animal抽象类
class Cat extends Animal {
	// 实现shout()方法
	public void shout() {
		System.out.println("喵喵……");
	}
}

// 定义Dog类继承Animal抽象类
class Dog extends Animal {
	// 实现shout()方法
	public void shout() {
		System.out.println("汪汪……");
	}
}

public class Test {
	public static void main(String[] args) {
		Animal an1 = new Cat();// 创建Cat对象,使用Animal类型的变量an1引用
		Animal an2 = new Dog();// 创建Dog对象,使用Animal类型的变量an2引用
		an1.shout();
		an2.shout();
	}
}

运行结果:

喵喵……
汪汪……

定义了两个继承Animal的类Cat和Dog,并在Cat类和Dog类中重写了Animal类中的shout()方法。创建了Cat类对象和Dog类对象,并将Cat 类对象和Dog类对象向上转型成了Animal类型的对象,然后通过Animal类型的对象anl和an2调用shout()方法,对象an1和an2调用的分别是Cat类和Dog类中的shout()方法。

对象类型的转换

(1)向上转型:子类对象→父类对象。

(2)向下转型:父类对象→子类对象。

对于向上转型,程序会自动完成,而向下转型时,必须指明要转型的子类类型。

对象向上转型:

文类类型  文类对象=子类实例;

对象向下转型:

父类类型  父类对象=子类实例;

了类类型  子类对象=   (子类)父类对象;
class Animal {
	public void shout() {
		System.out.println("喵喵……");
	}
}
// Dog类
class Dog extends Animal {
	// 重写shout()方法
	public void shout() {
		System.out.println("汪汪……");
	}

	public void eat() {
		System.out.println("吃骨头……");
	}
}

public class Test {
	public static void main(String[] args) {
		Animal dog = new Dog();// 创建Dog对象
		dog.shout();
	}
}

运行结果:

汪汪……

对象发生了向上转型关系后,所调用的方法一定是被子类重写过的方法。

父类Animal的对象an是无法调用Dog类中的eat()方法的,因为eat()方法只在子类中定义,没有在父类中定义。

 在进行对象的向下转型前,必须发生对象向上转型

public class Test {
	public static void main(String[] args) {
		Animal an = new Dog();// 创建Dog对象
		Dog dog=(Dog)an;
		dog.shout();
		dog.eat();
	}
}

运行结果:

汪汪……
吃骨头……

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值