Java面向对象与面向过程
面向过程(Procedure Oriented)和面向对象(Object Oriented,OO)都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。早期先有面向过程思想,随着软件规模的扩大,问题复杂性的提高,面向过程的弊端越来越明显的显示出来,出现了面向对象思想并成为目前主流的方式。两者都贯穿于软件分析、设计和开发各个阶段,对应面向对象就分别称为面向对象分析(OOA)、面向对象设计(OOD)和面向对象编程(OOP)。C语言是一种典型的面向过程语言,Java是一种典型的面向对象语言。
相同点:两者都是解决问题的思维方式,代码组织的方式。
区别:从特征上面向对象有三个特征即:封装性、继承性、多态性。而面向过程没有继承性、多态性,解决问题上:
宏观上使用面向对象,微观上使用面向过程,面向过程适合简单、不需要协作的事务。
联系:面对对象包括面对过程,两者相辅相成。
Java继承
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承可以理解为一个对象从另一个对象获取属性的过程。在JAVA中,类的继承是单一的,即一个子类只拥有一个父类。列如:如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子类,类C是从类A继承而来的。
继承关键字:extends implements
通过使用关键字extends,子类可以继承父类的除private属性外所有的属性。
通过使用关键字implements,在类在继承接口的情况下使用
package java05;
/**
* 继承
* @author Administrator
*
*/
public class JavaInherit {
public static void main(String[] args) {
Student stu1=new Student("java", "张三", 168);
stu1.rest();
stu1.study();
//instanceof 二元运算符,左边对象右边类
System.out.println(stu1 instanceof Person);
System.out.println(stu1 instanceof Student);
}
}
//父类 Personn类
class Person{
String name;
int height;
public void rest() {
System.out.println("休息一会!!");
}
}
//继承关键字extends 子类Student类
class Student extends Person{
String major;
public void study() {
System.out.println("我在蓝云学习java");
}
public Student(String major,String name,int height) {
super();
this.major = major;
this.height=height;
this.name=name;
}
}
注:
1.父类也称作超类、基类、派生类等。
2.Java中过于复杂,系统难于维护。
3.Java中类没有多继承,接口有多继承。 只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链
4.子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
5.如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。
Java 重写(Override)与重载(Overload)
重写(Override):重写是子类对父类的允许访问的方法的实现过程进行重新编写!返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。在面向对象原则里,重写意味着可以重写任何现有方法
package java05;
/**
* 方法重写
* @author Administrator
*
*/
public class JavaMethodOverride {
public static void main(String[] args) {
Vehicle v1=new Vehicle();
Vehicle v2=new Horse();
Vehicle v3=new Plane();
v1.run();
v2.run();
v3.run();
v1.stop();
v3.stop();
}
}
//父类
class Vehicle{
//run方法
public void run() {
System.out.println("跑");
}
//stop方法
public void stop() {
System.out.println("停止不动");
}
}
//子类1
class Horse extends Vehicle{
//重写方法run
public void run() {
System.out.println("跑等飞快");
}
}
//子类二
class Plane extends Vehicle{
public void run() {
System.out.println("天上飞!!!");
}
public void stop() {
System.out.println("坠机了!!!!!");
}
}
方法重写的规则
* 参数列表必须完全与被重写方法的相同; 返回类型必须完全与被重写方法的返回类型相同;
* 子类方法的访问权限必须大于或等于父类方法的访问权限。例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected。
* 父类的成员方法只能被它的子类重写。 声明为final的方法不能被重写。 声明为static的方法不能被重写,但是能够被再次声明。
* 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
* 子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
* 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,
* 反之则可以。 构造方法不能被重写。 如果不能继承一个方法,则不能重写这个方法。
重载(Overload):重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。只能重载构造函数
package javabiji;
/**
* 重载
*/
import org.omg.CORBA.PUBLIC_MEMBER;
public class JavaOverLoad {
public static void main(String[] args) {
//已见过的方法重载
System.out.println();
System.out.println(1);
System.out.println(3.5);
}
/*求和方法*/
public static int add(int n1,int n2) {
int sum=n1+n2;
return sum;
}
/*方法名相同,参数列表不同, 构成重载*/
public static int add(int n1,int n2,int n3) {
int sum=n1+n2+n3;
return sum;
}
/*方法名相同,参数类型不同,构成重载*/
public static double add(double n1,int n2) {
double sum=n1+n2;
return sum;
}
/*编译错误,只有返回值不同,不构成重载*/
// public static double add(int n1,int n2) {
// double sum=n1+n2;
// return sum;
// }
/*编译错误,只有参数名称不同,不构成重载*/
// public static double add(int n2,int n1) {
// double sum=n1+n2;
// return sum;
// }
}
重载规则
*被重载的方法必须改变参数列表;
*被重载的方法可以改变返回类型;
*被重载的方法可以改变访问修饰符;
*被重载的方法可以声明新的或更广的检查异常;
*方法能够在同一个类中或者在一个子类中被重载。
Java多态
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。现实生活中,同一个方法,具体实现会完全不同。比如:同样是调用人的“休息”方法,张三是睡觉,李四是旅游,赵程序员是敲代码
多态的优点
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
多态存在的三个必要条件
1、继承
2、重写
3、父类引用指向子类对象
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
package javabiji;
/**
*面向对象的多态
* @author Administrator
*
*/
public class JavaPolym {
public static void main(String[] args) {
Animal a1=new Cat();//向上可以自动转型
//传的是哪一个类型就调用哪一个方法,大大提高了程序的可扩展类型
animalCry(a1);
Animal a2=new Dog();
animalCry(a2);//a2为编译类型,Dog对象才是运行过时类型
//片写程序时,如果想调用运行时类型的方法,只能进行强制性类型转换,否则编译报错
// Dog dog =(Dog)a2;
// dog.seeDoor();
//向下转型
// Animal c=new Cat();
// Dog d3=(Dog)c;
// d3.seeDoor();
}
//有了多态,只需要让增加的这个类继承Animal类就可以了
static void animalCry(Animal a) {
a.shout();
}
/*
* //如果没有多态就需要写很多的方法重载,每增加同一种动物就需要重载一种动物的shout方法
* static void animalCry(Dog d) {
* d.shout();
* }
* static void animalCry(Cat c) {
* c.shout();
* }
*/}
class Animal{
public void shout() {
System.out.println("动物叫声!");
}
}
class Cat extends Animal{
public void shout() {
System.out.println("汪 汪 汪!");
}
}
class Dog extends Animal{
public void shout() {
System.out.println("喵喵喵!");
}
public void seeDoor() {
System.out.println("看门中");
}
}
输出结果:
汪 汪 汪!
喵喵喵!
Java封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指,一种将抽象性函式接口的实作细节部份包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
1. 良好的封装能够减少耦合。
2. 类内部的结构可以自由修改。
3. 可以对成员变量进行更精确的控制。
4. 隐藏信息,实现细节。
实现Java封装的步骤
1. 修改属性的可见性来限制对属性的访问(一般限制为private),例如:
public class Person {
private String name;
private int age;
}
这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
以上实例中public方法是外部类访问该类成员变量的入口。
通常情况下,这些方法被称为getter和setter方法。
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。 注意!!! boolean类型的get()方法开头是is!!!
通过如下的例子说明EncapTest类的变量怎样被访问:
/* F文件名 : RunEncap.java */
public class RunEncap{
public static void main(String args[]){
EncapTest encap = new EncapTest();
encap.setName("James");
encap.setAge(20);
encap.setIdNum("12343ms");
System.out.print("Name : " + encap.getName()+ " Age : "+ encap.getAge());
}
}
Java接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在Java中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在.java结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在.class结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了static和final变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多重继承。
接口的声明
接口的声明语法格式如下:
[可见度] interface 接口名称 [extends 其他的类名] {
// 声明变量
// 抽象方法
}
Interface关键字用来声明一个接口。下面是接口声明的一个简单例子。
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何类型 final, static 字段
//抽象方法
}
接口有以下特性:
1. 访问修饰符:只能是public或默认。
2. 接口名:和类名采用相同命名机制。
3. extends:接口可以多继承。
4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
5. 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。
实例
/* 文件名 : Animal.java */
interface Animal {
public void eat();
public void travel();
}
接口的实现
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
实现一个接口的语法,可以使用这个公式:
... implements 接口名称[, 其他接口, 其他接口..., ...] ...
实例
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
以上实例编译运行结果如下:
Mammal eats
Mammal travels
重写接口中声明的方法时,需要注意以下规则:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
- 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
在实现接口的时候,也要注意一些规则:
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
下面的Sports接口被Hockey和Football接口继承:
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。
接口的多重继承
在Java中,类的多重继承是不合法,但接口允许多重继承,。
在接口的多重继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:
public interface Hockey extends Sports, Event
以上的程序片段是合法定义的子接口,与类不同的是,接口允许多重继承,而 Sports及 Event 可能定义或是继承相同的方法
没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
Java包(package)
为了更好地组织类,Java提供了包机制,用于区别类名的命名空间。
包的作用
- 1 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 2 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 3 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
Java使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
包语句的语法格式为:
package pkg1[.pkg2[.pkg3…]];
例如,一个Something.java 文件它的内容
package net.java.util
public class Something{
...
}
那么它的路径应该是 net/java/util/Something.java 这样保存的。 package(包)的作用是把不同的java程序分类保存,更方便的被其他java程序调用。
一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
以下是一些Java中的包:
- java.lang-打包基础的类
- java.io-包含输入输出功能的函数
开发者可以自己把一组类和接口等打包,并定义自己的package。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。
由于package创建了新的命名空间(namespace),所以不会跟其他package中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。
创建包
创建package的时候,你需要为这个package取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个package的声明放在这个源文件的开头。
包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。
如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。
例子
让我们来看一个例子,这个例子创建了一个叫做animals的包。通常使用小写的字母来命名避免与类、接口名字的冲突。
在animals包中加入一个接口(interface):
/* 文件名: Animal.java */
package animals;
interface Animal {
public void eat();
public void travel();
}
接下来,在同一个包中加入该接口的实现:
package animals;
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
然后,编译这两个文件,并把他们放在一个叫做animals的子目录中。 用下面的命令来运行:
import关键字
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用"import"语句可完成此功能。
在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
package javabiji;
//导包方法一
import java.util.Date;
//import java.util.Scanner;
import java.util.*;//导包方法二 导入该包下的所有包,会降低编译速度,但是不会降低运行速度
public class JavaPackage {
public static void main(String[] args) {
//这里指的是java.sql.Date
Date now;
//java.sql.Date因为和ava.util.Date类同名,需要完整路径
java.util.Date now2=new java .util.Date();
System.out.println(now2);
//java.util包的非同名类不需要完整路径
Scanner input=new Scanner(System.in);
}
}
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。