上一篇文章我们是对面向对象的入门,现在我们来具体的看看面向对象的特征
面向对象的三大特征:
封装性(encapsulation)
继承性(inheritance)
多态性 (polymorphism)
我们先来看封装的特性
我们首先要了解它的概念是不,我们来看看它的概念
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问。
封装的好处
(1)只能通过规定的方法访问数据
(2)隐藏类的实例细节,方便修改和实现
(3)可以彻底隐藏方法的内部实现,仅仅提供一个调用的方法给其他人,让其他使用这个类的人不需要关心是如何实现的,只要知道该如何调用就行。
(4)隐藏方法的内部实现的好处,可以让保留调用方法不变的同时,随意修改类的结构,而不影响其他人运行结果。
(5)封装还会分开类的属性,将类的属性分成私有属性和公共属性。私有属性仅供类自身调用,和公共属性也仅提供一个供外部调用的方法。
(6)按照软件的术语,良好的封装是能够减少耦合。
这个我们了解了就来看看怎么去封装,也就是封装的实现步骤
封装的实现步骤
(1)修改属性的可见性设为(private)
(2)创建getter/setter方法(用于属性的读写)(通过这两种方法对数据进行获取和设对象通过调用这两种发方法实现对数据的读写)
(3)在getter/setter方法中加入属性控制语句(对属性值的合法性进行判断)
我们想要熟练的使用这个就必须知道在java中的权限修饰符
权限修饰符我们就直接上图
加上访问修饰符有时候可能会给操作数据带来不便,但可以在很大程度上保证数据的安全
一般地,我们会将成员属性声明为private,而将成员方法声明为public,但这样做并不是绝对的
有时候,类外部可能要操作到某些私有数据成员,那么就可以增加一个公有的方法,再由这个方法来操作私有数据,避免因类外部的误操作而造成的数据损坏
因为main方法要由类外部的虚拟机来调用,所以main方法必须声明成public
这个时候我们就要知道什么是访问权限了
访问权限:
无法从类的外部访问私有成员;
其它类的私有成员对于当前类也是隐藏的。
废话有点多,我们直接看代码
package com.fl.test;
/**
* 创建一个人的类
* @author 86185
*
*/
public class Person {
//姓名
private String name;
//年龄
private int age;
//性别
private String sex;
//创建set get 方法
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
package com.fl.test;
/**
* 测试类
* @author 86185
*
*/+
public class Test {
public static void main(String[] args) {
Person ps=new Person();
//通过set方法给name属性赋值
ps.setName("李浩");
//输出赋值的属性
System.out.println(ps.getName());
}
}
在代码中我们可以看到在我定义的人的类中我讲他的属性都封装起来了,只能通过我的set和get方法去对它操作
我们来看下访问权限的代码案例
//访问权限符的案例:
class Student {
private String name; //姓名,私有的,不可以从类外部直接访问
private int age; //年龄,私有的,不可以从类外部直接访问
private float weight; //体重,私有的,不可以从类外部直接访问
//吃饭的方法,公有的,可以从任何地方访问
public void dining() {
System.out.println("吃饱了...");
weight++; //dining方法是类内部成员,可以直接访问本类私有成员
}
//走路的方法,公有的,可以从任何地方访问
public void walk() {
System.out.println("走累了...");
weight--; //walk方法是类内部成员,可以直接访问本类私有成员
}
}
public class Test {
public static void main(String[] args) {
Student std = new Student(); //实例化一个Student对象
std.age = 18; //试图从类外部访问私有成员,将会报出一个错误
std.dining(); //允许访问公有成员
}
}
我们继续来看第二个特性,继承
1、最高层是最普遍的、最一般的情况,往下每一层都比上一层更具体,并包含有高层的特征,通过这样的层次结构使下层的类能自动享用上层类的特点和性质;
2、继承其实就是自动地共享基类中成员属性和成员方法的机制。
我们简单的理解就是儿子继承父亲的东西
在Java中实现继承需要使用到extends关键字;
实现继承的一般语法是:
[访问修饰符] class 派生类名 extends 基类名 {
成员列表
}
如:
class Student extends Person {
……
}
实现继承的案例
package com.fl.anli02;
/**
* 定义一个父亲的类
* @author 86185
*
*/
public class Father {
//定义一个有钱的方法
public static void money() {
System.out.println("我很有钱");
}
}
package com.fl.anli02;
/**
* 定义一个儿子的类并继承父亲的方法
* @author 86185
*
*/
public class Son extends Father{
//定义一个儿子的方法
public static void name() {
System.out.println("我是儿子的方法");
}
}
package com.fl.anli02;
/**
* 测试是否继承
* @author 86185
*
*/
public class Test {
public static void main(String[] args) {
// //实例化对象(父亲的)
// Father fa=new Father();
// fa.money();
//实例化对象(儿子的)
Son so=new Son();
//这是继承父亲的方法
so.money();
//这是本身的方法
so.name();
}
}
注意:
继承中的构造方法
父类中的构造方法不能被子类继承,即便它是public的;
父类的构造方法负责初始化属于它的成员变量,而子类的构造方法则只需考虑属于自己的成员变量,不必去关注父类的情况。
class ParentClass { //定义父类
public ParentClass() { //构造方法
System.out.println("这是父类的构造方法。");
}
}
class ChildClass extends ParentClass { //子类继承于父类
public ChildClass() { //构造方法
System.out.println("这是子类的构造方法。");
}
}
public class ConstructorTest { //该类用于容纳main方法
public static void main(String[] args) {
ChildClass cc = new ChildClass(); //实例化子类对象
}
}
构造方法的执行顺序
1、当实例化子类的对象时,必须先执行父类的构造方法,然后再执行子类的构造方法
2、如果父类还有更上级的父类,就会先调用最高父类的构造方法,再逐个依次地将所有继承关系的父类构造方法全部执行;
3、如果父类的构造方法执行失败,那么子类的对象也将无法实例化。
1、在实例化Circle类对象时,虚拟机一定会先调用其父类(Point类)的构造方法;
2、Point类的构造方法需要两个参数来初始化其成员,但此时并没有获得这两个参数,造成Point类的构造方法无法执行;
3、父类的构造方法执行失败从而导致子类(Circle类)的对象也无法创建;
4、问题的关键是:在实例化子类对象时,如何将参数传递给父类的构造方法?这将使用到super关键字。
1、子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法)。
2、java中只有单继承,没有像c++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。就像我们现实中,如果你有多个父母亲,那是一个多么混乱的世界啊。多继承,就是为了实现代码的复用性,却引入了复杂性,使得系统类之间的关系混乱。
3、java中的多继承,可以通过接口来实现
4、如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。
5、意味着,一个类只能有一个直接父类
方法重写
子类继承父类的一切东西.[Think In Java]
思考: 当子类的方法与父类方法一致,但是实现细节不同时,我们该如何设计?
1、在子类中可以根据需要对从基类中继承来的方法进行重写。
2、重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型。
3、重写方法不能使用比被重写方法更严格的访问权限。(由于多态)
Object
Object类是所有类的父类,不需要显式继承
作为用户自定义类型需要重写Object的方法
在这里我就要提一下一个关键字的使用。Super关键字
Super关键字有两种用途在java中
第一种用途是:
在子类的构造方法中,super关键字可以显式地调用父类的构造方法,用于将参数传递给它
其一般语法是:
super(实际参数);
需要注意的是:该语句必须是子类构造方法的第一条语句。
第二种用途是:
如果父类和子类中有同名成员,在子类中默认访问是属于自己的那一个成员;
super关键字可以明确地指定要访问父类中的成员;
其一般语法是:
super.成员名;
前提条件是:父类中的该成员不是private的。
这个就是我对java的继承的理解,我们来看最后一个特性
1、指对象执行相同的方法时,会产生不同行为特征。
2、同一个类在不同的场合下表现出不同的行为特征
多态的作用:
消除类型之间的耦合关系。
多态存在的三个必要条件:
继承;重写;父类引用指向子类对象(向上转型)。
当使用多态方式调用方法时,首先检查父类中是否有该方法。如果没有,则编译错误;如果有,再去调用子类的同名方法。
案例:
public class AnimalTest {
public static void main(String[] args) {
Animal a = new Cat();
a.eat();//访问成员方法,编译看左边,运行看右边
System.out.println(a.name);//访问成员变量,编译看左边,运行看左边
//a.tree();报错,多态的弊端,不能访问子类特有的功能
Animal a2 = new Dog();
a2.function();//访问静态方法,编译看左边,运行看左边
//静态方法不能算方法的重载
}
}
class Animal{
String name = "动物";
public void eat(){
System.out.println(name+"能够吃东西");
}
public static void function(){
System.out.println("动物的静态方法");
}
}
class Cat extends Animal{
String name = "猫";
String color = "黑色";
public void eat(){
System.out.println(name+"能够吃东西");
}
public void tree(){
System.out.println("猫能够爬树");
}
}
class Dog extends Animal{
String name = "狗";
public void eat(){
System.out.println(name+"能够吃东西");
}
}
多态的优点:
1.可替换性
即多态对已存在的代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性
即多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。新添加的子类更容易获得多态功能。例如,在实现圆锥、半圆锥以及半球体的多态的基础上,很容易添加球体类的多态性。
3.接口性
即超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现。
4.灵活性
即灵活多样的操作,提高了使用的效率。
5.简化性
即简化了对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作方面。
多态的成员特点:
成员变量:编译运行都看左边(父类);
成员方法:编译看左,运行看右(子类)。
这个时候我们就要了解一下多态的类型的转换
多态有两种类型转换
1、向上转型:
当有子类对象赋值给一个父类引用时,便是向上转型。-------多态本身就是向上转型的过程
父类类型 变量名 = new 子类类型();
案例
class A{
public void fun1() {
System.out.println("父类的方法");
}
};
class B extends A{
public void fun1() {
System.out.println("子类的方法");
}
};
public class PolDemo01 {
public static void main(String[] args) {
// TODO Auto-generated method stub
B b = new B();
A a = b;
a.fun1();
}
}
2、向下转型:
一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。
在多态的基础上才可以的,也就是得先向上再向下的!
向下转型后调用对象和方法时,都是调用的子类的方法;也可以调用子类独有的普通方法。
子类类型 变量名 = (子类类型) 父类类型的变量;
class A{
public void fun1() {
System.out.println("父类的方法");
}
};
class B extends A{
public void fun1() {
System.out.println("子类的方法");
}
};
public class PolDemo02 {
public static void main(String[] args) {
A a = new B();
B b = (B) a;
b.fun1();
}
}
多态的优劣:
1、什么时候用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
2、什么时候用向下转型:
当要使用子类特有功能时,就需要使用向下转型。
向下转型的好处:可以使用子类特有功能。
弊端是:
需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。(有可能转到另外的一个子元素上,这时候就需要instanceof 判定下是不是所需要的数据类型)
实现多态步骤总结
1、子类重写父类的方法
2、编写方法时,使用父类定义的方法
3、运行时,根据实际创建的对象类型动态决定使用哪个方法
这些是我对java中的三大特征的理解,大家有什么要补充的可以在下面评论哦