黑马程序员_java 面向对象三大特征

 

 

Java是面向对象的程序设计语言,主要表现为java完全支持面向对象的三种基本特征:封装、继承、多态。

封装:

封装是面向对象三大特征之一,它指的是将对象的实现细节隐藏在对象内部,不允许外部直接访问。通过提供的公有方法供外部使用提高了程序的安全性,和代码的可维护性。通过访问修饰符来实现封装。Java提供了4中访问修饰符:privatedefaultprotectedpublic,访问级别按照抒写顺序从小到大。具体访问权限如下:

private(当前类访问权限):如果类中的成员使用private修饰,则被修饰的成员只能在此类中供内部访问。因此一般使用private修饰成员属性,实现封装。

default(包访问权限):这里的default是指默认不写,不是修饰符叫default。如果类中的成员或者定义类时不适用任何访问修饰符,就称为包访问权限,default修饰的成员和类可以被同一包下的其他类访问。

protected(子类访问权限):如果一个成员使用protected修饰,该成员可以被同一包中的其他类访问,也可以被不同包中的子类访问,如果方法被protected修饰,一般是希望子类重写该方法。

public(公共访问权限):被public修饰的成员或类,该成员和类可以被所有类访问。访问权限最大。

访问修饰符用于控制一个类的成员是否可以被其他类访问,因此局部变量不能使用访问修饰符,因为局部变量的作用范围只在所在的一对大括号中有效。对于类而言(此篇中的类不是内部类),只能使用public和默认访问修饰符修饰,因为类没有处于任何类的内部,所以不能使用privateprotected修饰。

封装代码示例:

public class Sample {

/**

 * 将属性私有化,不允许其他类直接访问

 */

private String name;

private int age;

/**

 * Sample类的无参构造器

 */

public Sample(){}

/**

 * Sample类的有参构造器

 * @param name

 * @param age

 */

public Sample(String name, int age) {

this.name = name;

/**

 * 通过判断,保证录入的年龄合法

 */

if(age < 0 || age > 120){

System.out.println("输入的年龄不合法");

return;

}

this.age = age;

}

/**

 * 对外提供获取name的方法

 * @return 返回name

 */

public String getName() {

return name;

}

/**

 * 对外提供设置name的方法

 * @param name 该参数指定对象的name

 */

public void setName(String name) {

this.name = name;

}

 

/**

 * 对外提供获取age的方法

 * @return 返回age

 */

public int getAge() {

return age;

}

 

/**

 * 对外提供该方法,设置age的方法

 * @param age 该参数指定对象的age

 */

public void setAge(int age) {

/**

 * 通过判断,保证录入的年龄合法

 */

if(age < 0 || age > 120){

System.out.println("输入的年龄不合法");

return;

}

this.age = age;

}

}

上面程序演示外部不能直接对agename进行操作,而是通过Sample类对外提供的getset方法来访问,提高了程序的可维护性。

使用访问修饰符的基本原则:

1、类中的属性一般使用private修饰,static修饰的类变量除外。用于辅助实现其他方法的方法也用private修饰。

2、如果一个类是其他类的父类,希望其他类重写该类里的方法,而不是被外界直接调用,一般用protected修饰

3、希望暴露出来给其他类自由调用的方法应该用public修饰,因此一般构造器用public修饰

继承:

关键字:extends

概述:类本身就是一种抽象行为,它是从对象中抽取出共性的属性,来描述某一类事物。而类与类之间也存在共性的属性和方法,将这些共性向上抽取到一个单独的类中(父类或基类,超类),那么多个类(子类)就无需重复定义,只要继承该类即可,子类可以直接访问父类中的非私有属性和行为。提高了代码的复用性

例:public class b{}

 public class A extends B{}

好处:提高了代码的复用性,让类与类之间产生了关系,为多态提供了先决条件

特点:java只支持单继承,不支持多继承 

例:public class A extends B{} //   OK

Public class A extends B,C{}//  Error

  为什么不支持多继承?

  因为多继承容易带来安全隐患,当多个父类中定义了相同功能,而功能主题不同时,子类对象不确定运行哪一个方法。但java保留了这种机制,用另一种表现形式来实现,即多实现。不过java支持多层继承,也就是一个体系

例:public class A extends B{}

 Public class B extends C{}

 

Ø 如何使用一个体系中的功能?

查阅父类功能,创建最子类对象

Ø 为什么是创建子类对象?

1、有可能父类不能创建对象(如:抽象类,接口)

2、创建子类对象可以更多的功能,包含了子类

 

注意:不要纯粹为了简化代码而继承,必须是类与类之间有所属关系才可以继承(is a)

子父类中属性的特点:

当调用非私有属性时,如果子类中有就调用子类中的属性,如果子类中没有就调用父类中的属性;在子类内部访问时,如果子父类中出现同名属性,要想访问访问父类属性就必须加上super,用法:super.属性名,如果不同名可以直接访问

子父类中函数的特点:

当子父类中出现一模一样的函数时,子类对象调用该函数,会运行子类中的函数,如同父类中的函数被覆盖了一样,这种情况是函数的另一个特征:重写(覆盖)

例:public class Test{

   主函数(){

Zi z = new Zi();

z.show();  //

   }

}

   Class Fu{

Public void show(){Sop(父类函数运行了)}

//如果把void改为int,编译失败,因为不仅实现不了重写   而且违背了函数重载的特性

}

Class Zi{

Public void show(){Sop(子类函数运行了)}

}

  重写:子类覆盖父类必须保证子类访问权限大于等于父类(否则编译失败),并且参数列表和返回值类型以及方法名一模一样才能实现覆盖。记住:重写子父类方法要一模一样,重载只是参数列表不一样注意事项:静态只能覆盖静态因为覆盖必须是子父类中的函数一模一样,如果去掉其中一个类中的static,编译会不通过,因为违反了函数重载的特性

例:

public class Test {

public static void main(String[] args)

{

Zi z = new Zi();

z.show();  //运行结果:子类中的函数运行了

}

}

 class  Fu

{

static void show()

{

System.out.println("父类中的函数运行了");

 

}

}

class Zi extends Fu

{

 static void show()

{

 

System.out.println("子类中的函数运行了");

}

}

 

子父类中构造函数的特点:

子父类中的构造函数不能实现重写(覆盖),因为子父类中的函数名不可能一样,不符合重写的要求。

例:Class  A{

A(){Sop(父类构造器)}

//如果A(有参数),编译出错,因为子类中没有创建与之 对应的Super

   }

 Class B extends A{

B(){

Super();

Sop(子类构造器)

}

}

//输出结果:父类构造器

  子类构造器

Ø 为什么创建子类对象时,会先运行父类构造器?

因为子类构造器的第一行有一条隐式语句super();super是父类的引用,super()会访问父类空参数的构造函数,并且所有子类构造函数中的第一行默认都是super();当父类构造函数中没有空参数的构造函数时,必须手动建立super(与之对应的参数)

代码示例:

public class Person {

 String name;

 int age;

public Person(){}

public Person(String name, int age)

{

this.name = name;

this.age = age;

}

public void sleep()

{

System.out.println("睡觉");

}

public String toString()

{

return name + "  " + age;

}

}

class Student extends Person

{

private String stuNo;

public Student(){}

public Student(String name, int age, String stuNo)

{

//通过super显示的给父类构造器传参

super(name, age);

this.stuNo = stuNo;

}

public String toString()

{

return name + "  " + age"  " + stuNo;

}

}

Ø 为什么子类一定要访问父类中的构造函数?

因为父类中的非私有数据子类可以直接获取,所以子类在对象建立时,需要先查看父类是如何对这些数据进行初始化的

多态:

初步理解:其实就是子父类(接口或实现类)中的类型转换。子类转父 类即小转大,和变量一样会自动类型转换,或称之为类型 提升,父类转子类即大转小,要做强制类型转换

程序中的体现:父类或接口的引用指向或者接收子类的对象,不能是子类引用指向父类对象,自始至终多态都是子类对象在做变化

好处:大大提高了程序的扩展性

前提:1、必须是类与类之间有关系,要么继承,要么实现(接口)

2、必须存在覆盖

弊端:只能使用父类中引用指向父类中的成员,但是会由子类中方法对其覆盖,如果掉用父类中不存在的方法,会编译失败

属性的特点:属性不是多态的,当子父类中出现相同的属性时,以左边的引用为准

例:class A{

Int num = 3;

 }

Class B extends A

{

Int num = 5;

}

Main(){

A a = new B;

Sop(a.num)    //输出3

B b = new B;

Sop(b.num)    //输出5

}

非静态私有函数的特点:

1、编译时期:参阅引用变量所属类(父类)中是否有调用方法,如果有编译通过,如果没有编译失败

      2、运行时期:如果子类中复写了父类中的方法,运行子类中的方法,如果子类没有复写,运行父类自己的方法

静态函数的特点:和属性一样,static方法不是多态的,无论是编译 还是运行都参考左边。代码示例:

public class DemoTest {

public static void main(String[] args) {

A a = new B();

a.show(); //运行结果:父类函数 

}

}

class A

{

public static void show()

{

System.out.println("父类函数");

}

}

class B extends A

{

public static void show()

{

System.out.println("子类函数");

}

}

Instanceof运算符:

Instanceof的前面是一个引用类型变量,后面是一个类,用于判断前面的对象是否是后面类的实例,如果是返回true,否则返回false。避免了ClassCastException异常。

 

示例代码一:

public class DemoTest {

public static void main(String[] args) {

Person p1 = new Person(4);

Person p2 = new Person(3);

A a = new A();

System.out.println(p1.equals(p2));

System.out.println(p1.equals(a));

}

}

class A{}

class Person

{

int age;

Person(int age)

{

this.age = age;

}

public boolean equals(Object o)

{

if(o instanceof Person)

{

Person p = (Person)o;

if(this.age > p.age)

{

return true;

}else

{

return false;

}

}else

return false;

}

}

代码举例二:

public class Test {

public static void main(String[] args)

{

Animal a = new Cat();//类型提升,向上转型。和基本数据类型一样,

//:byte a = 3; int b = a;

function(a);

//function(new Cat);

}

public static void function(Animal a)

{

a.eat();

//如果调用父类中不存在的方法,编译失败,因为父类中不存在子类中的特有方法,但如果要使用子类中

//的特有方法怎么做呢?将父类的引用强制转换为子类类型

if(a instanceof Cat)

 

{

Cat c = (Cat)a;

c.catchMouse();

}else if(a instanceof Dog)

{

Dog d = (Dog)a;

d.kanHouse();

}}

}

public interface Animal {

public abstract void eat();

}

public class Cat implements Animal {

public void eat()

{

System.out.println("吃鱼");

}

public void catchMouse()

{

System.out.println("抓老鼠");

}

}

public class Dog implements Animal {

public void eat()

{

System.out.println("吃骨头");

}

public void kanHouse()

{

System.out.println("看家");

}

}

posted @ 2013-03-08 14:37  谢冬  阅读(546)  评论(0编辑  收藏  举报