Java的面向对象的理解
1.思想简述:
面向对象是一种思想。它将数据和操作数据的方法封装在对象中,从而使对象有了一些功能,也就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节;这种思想将数据和操作数据的方法交给对象去管理,当我们需要某些功能的时候,只要找到能够实现该功能的对象然后让他去做就好了。这样的好处是我们不需要知道这个对象是如何实现这个功能的,我们要的是一个结果。
举个例子就比较容易理解了:
假设你是一个不会做饭的人,你肚子饿了,那该怎么办呢?
当然是去找个会做饭的人做,你不需要知道饭是怎么做的。只要等上二十多分钟你就有饭吃了。
面向对象的好处:
1.面向对象这种思想符和人们思考方式的一种思考方法
2.利用面向对象我们可以指挥大型软件的搭建
2.Java面向对象的三大特性:封装、继承、多态
A.封装
1.概述:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
2.好处:
- 只能通过规定的方法访问数据
隐藏类的方法实现细节,方便修改和优化
3.如何封装
看下面代码,可以知道如何进行封装
public class Dog {
private String name;//把name的可见性设为private
public Dog(String name) {
this.name=name;
}
//通过setName方法修改name的值
public void setName(String name) {
this.name=name;//使用this关键字可以区分参数名和类的数据成员
}
//通过getName方法获得name的值
public String getName() {
return this.name;
}
}
4.Java的四种访问修饰符
客户端程序员:在其应用程序中使用数据类型的类消费者。他主要是利用已有的类来快速开发其应用程序
类创建者:他主要是创建新数据类型的类
访问控制符存在的原因:1.让客户端程序员无法触及他们不应该的部分 2.允许类的创建者修改类的内部工作方式,不用担心会影响客户端程序员写的代码
Java的四种访问修饰符:public、proteced、default、private
访问权限越小,安全性越高
访问权限 | 类 | 包 | 子类 | 其它包 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
从表格中可以看出来,封装性从上到下越来越好
5.this关键字
1.表示当前对象
this.属性 表示当前对象的属性
this.方法 表示当前对象的方法
return this;表示返回当前对象自己
2.封装对象的属性时,经常会用到this关键字
3.当方法的参数和类的数据成员相同时,使用this关键字可以加以区分
6.内部类
顾名思义,就是在一个类的内部定义了一个类。与之对应,包含内部类的类叫做外部类。
那为什么要在一个类里面有再定义一个类呢?这样子不是搞得很复杂了吗
简单说一下内部类的作用:
1.内部类提供了更好的封装,可以把内部类隐藏在外部类之中,不允许同一包中的类访问内部类
2.内部类可以访问外部类的所有数据,包括私有数据
3.内部类实现的功能外部类也可以实现,只是使用内部类更加方便
内部类可以分为以下几种:
1.成员内部类
2.静态内部类
3.方法内部类
4.匿名内部类
B.继承
在《Think in java》中有这样一句话:复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言,仅仅能够复制代码并对加以改变是不够的,它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码使我们程序员一直在追求的,现在我来介绍一种复用代码的方式,也是java三大特性之一---继承。
继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类,新类把父类能继承的东西都继承了。通过使用继承我们能够非常方便地复用以前的代码,能够大大的提高开发的效率。
继承所描述的是“is-a”的关系,如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类,A是继承者称之为子类或者派生类
同时在使用继承时需要记住三句话:
1、子类拥有父类非private的属性和方法。
2、子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
3、子类可以用自己的方式实现父类的方法。(以后介绍)。
当然,讲到继承一定少不了这三个东西:构造器、protected关键字、向上转型。
构造器
子类可以继承父类的属性和方法,除了那些private的外还有一样是子类继承不了的---构造器。对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用super()即可。
对于子类而已,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。
对于继承而已,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。
protected关键字
private访问修饰符,对于封装而言,是最好的选择,但这个只是基于理想的世界,有时候我们需要这样的需求:我们需要将某些事物尽可能地对这个世界隐藏,但是仍然允许子类的成员来访问它们。这个时候就需要使用到protected。
对于protected而言,它指明就类用户而言,他是private,但是对于任何继承与此类的子类而言或者其他任何位于同一个包的类而言,他却是可以访问的。
尽管可以使用protected访问修饰符来限制父类属性和方法的访问权限,但是最好的方式还是将属性保持为private(我们应当一致保留更改底层实现),通过protected方法来控制类的继承者的访问权限。
向上转型
将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。这就是为什么编译器在“未曾明确表示转型”活“未曾指定特殊标记”的情况下,仍然允许向上转型的原因。
既然继承有那么多的好处,那我们是否能够随便用继承呢?送你一句话:慎用。
首先我们需要明确,继承存在如下缺陷:
1、父类变,子类就必须变。
2、继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。
3、继承是一种强耦合关系。
所以说当我们使用继承的时候,我们需要确信使用继承确实是有效可行的办法。那么到底要不要使用继承呢?《Think in java》中提供了解决办法:问一问自己是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承。
C.多态
什么是多态呢?
对于面向对象而言,多态分为编译时多态和运行时多态这两种。
编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。
运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。
下面来重点说一下运行时多态
运行时多态
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
java实现多态有三个必要的条件:继承、重写、向上转型
继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
多态的实现方式:继承和接口
public interface Eat {
public void eat();
}
public class Father implements Eat{
private String name;
public Father() {
this.setName("father");
}
public void setName(String name) {this.name=name;}
public String getName() {return this.name;}
public void read() {
System.out.println(getName()+"看"+"<<春秋>>");
}
/*
* 重写toString
* @see java.lang.Object#toString()
*/
public String toString() {return "father";}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println(getName()+"吃水果");
}
}
public class Son1 extends Father {
public Son1() {
this.setName("儿子1");
}
/*
* 重写父类方法,实现多态
* @see jich.Father#read()
*/
public void read() {
System.out.println(getName()+"看"+"<<雅舍>>");
}
/*
* 重写toString()
* @see jich.Father#toString()
*/
public String toString() {
return this.getName();
}
/*
* 重写接口eat方法
* @see jich.Father#eat()
*/
public void eat() {
System.out.println(getName()+"吃米饭");
}
}
public class Son2 extends Father {
public Son2() {
this.setName("儿子2");
}
/*
* 重写父类方法,实现多态
* @see jich.Father#read()
*/
public void read() {
System.out.println(getName()+"看"+"<<狂人日记>>");
}
/*
* 重写toString()
* @see jich.Father#toString()
*/
public String toString() {
return this.getName();
}
/*
* 重写接口
* @see jich.Father#eat()
*/
public void eat() {
System.out.println(getName()+"吃面条");
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father[] father=new Father[2];
Son1 son1=new Son1();
Son2 son2=new Son2();
father[0]=son1;
father[1]=son2;
Object o=new Father();
for(int i=0;i<2;i++) {
father[i].read();
}
System.out.println(o.toString());
Eat[] eat=new Eat[3];
eat[0]=father[0];
eat[1]=father[1];
eat[2]=new Father();
for(int i=0;i<3;i++)eat[i].eat();
}
}
对于继承实现的多态可以总结为:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。
如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层次的方法。
接口实现的多态
继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。
继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
写在文章末尾,上个学期学完了Java。现在写这一篇博文笔记,系统总结了Java面向对象的思想,收获挺多的。参考此博文,该博主写的博文对我的启发蛮大的。