黑马程序员_java 面向对象三大特征
Java是面向对象的程序设计语言,主要表现为java完全支持面向对象的三种基本特征:封装、继承、多态。
封装:
封装是面向对象三大特征之一,它指的是将对象的实现细节隐藏在对象内部,不允许外部直接访问。通过提供的公有方法供外部使用提高了程序的安全性,和代码的可维护性。通过访问修饰符来实现封装。Java提供了4中访问修饰符:private、default、protected、public,访问级别按照抒写顺序从小到大。具体访问权限如下:
private(当前类访问权限):如果类中的成员使用private修饰,则被修饰的成员只能在此类中供内部访问。因此一般使用private修饰成员属性,实现封装。
default(包访问权限):这里的default是指默认不写,不是修饰符叫default。如果类中的成员或者定义类时不适用任何访问修饰符,就称为包访问权限,default修饰的成员和类可以被同一包下的其他类访问。
protected(子类访问权限):如果一个成员使用protected修饰,该成员可以被同一包中的其他类访问,也可以被不同包中的子类访问,如果方法被protected修饰,一般是希望子类重写该方法。
public(公共访问权限):被public修饰的成员或类,该成员和类可以被所有类访问。访问权限最大。
访问修饰符用于控制一个类的成员是否可以被其他类访问,因此局部变量不能使用访问修饰符,因为局部变量的作用范围只在所在的一对大括号中有效。对于类而言(此篇中的类不是内部类),只能使用public和默认访问修饰符修饰,因为类没有处于任何类的内部,所以不能使用private和protected修饰。
封装代码示例:
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;
}
}
上面程序演示外部不能直接对age和name进行操作,而是通过Sample类对外提供的get和set方法来访问,提高了程序的可维护性。
使用访问修饰符的基本原则:
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("看家");
}
}