黑马程序员-----面向对象的特性封装、继承、多态、抽象

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

1.面向对象的特性

1.1封装

1.1.1封装的特点

1.直接对外部暴露成员变量是很不安全的,这时可以将成员变量“私有化”,对外提供公有的 get和set方法;

2.封装的好处:

                            1)隐藏实现细节,提供公共的访问方式

                            2)提高了代码的复用性

                            3)提高安全性。

3.封装的原则:

                            1)将不需要对外提供的内容都隐藏起来。

                            2)把属性隐藏,提供公共方法对其访问。

1.1.2封装的代码解释

/*

封装:

表现:

1,函数就是一个最基本封装体。

2,类其实也是一个封装体。

从以上两点得出结论:

好处:

1,提高了代码的复用性。

2,隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。

3,提高了安全性。

它也是面向对象思想的特征之一。

共有三个特征:封装,继承,多态。

举例:机箱。隐藏了办卡设备的细节,对外提供了插口以及开关等访问内部细节的方式。

*/

//描述人。Person

//属性:年龄。

//行为:说话:说出自己的年龄。

/*

总结:

类中不需要对外提供的内容都私有化,包括属性和行为。

selectSort(int[] arr)

{

         swap(

}

bubbleSort(int[] arr)

{

         swap(

}

private swap(int[] arr,int a,int  b)

{

}

重点:以后再描述事物,属性都私有化,并提供setXxxgetXxx方法对其进行访问。

*/

class Person
{
	//属性:
	private int age;//age就是被修饰为了private私有。也就是被隐藏了。这就是封装的一种体现。
	//行为:
	void speak()
	{
		System.out.println("age="+age);
	}

	/*
	年龄已被私有,错误的值无法赋值,可是正确的值也赋值不了,不行。
	咋办,按照之前所学习的封装的原理,隐藏后,还需要提供访问方式。
	通过方法的方式,让其他程序访问到,就可以了。更重要的是可以在方法中加入逻辑判断。
	
	记住:对变量的访问操作有两个动作:赋值(设置 set),取值(获取 get)
	所以,对私有的变量访问的方式就是 set变量  get变量--> setAge  getAge
	*/
	//定义对age赋值的方法。
	void setAge(int a)
	{
		//加入逻辑判断。
		if(a>0 && a<130)
			age = a;
		else
//			System.out.println("对不起,您的年龄数值 "+a+" 是非法的。");
//			throw new RuntimeException("对不起,您的年龄数值 "+a+" 是非法的。");//异常!一旦出现,程序结束。需要修正代码。
	}

	//定义一个获取age值的方法。
	int getAge()
	{
		return age;
	}

}

class PersonDemo
{
	public static void main(String[] args)
	{
		//测试Person.class。
		//创建对象。
		Person p = new Person();

		/*
		赋值-20是可以的,因为age属性是int类型,但是确不符合现实生活中的事物。
		怎么解决这个问题呢?
		不让它访问就哦了。怎么在代码上实现呢?需要使用一个Java中的关键字也是一个修饰符 private(私有,权限修饰符)
		记住:私有仅仅是封装的体现形式而已。

		*/
//		p.age = -20;//age不能在person类以外的程序中直接访问了。

		//演示对age设置和获取方法的体现。
		p.setAge(-20);
		int a = p.getAge();
		System.out.println("a="+a);
//		p.speak();
	}
}

1.2继承

1.2.1继承的特点

1,继承的好处。★★★★★

2,Java中的单继承和多继承的区别,以及多继承的好处,为什么不直接支持?★★★★★

                   多继承好处:更加的扩展了子类的功能。这个机制非常好。

                   但是有弊端:导致调用多父类中的相同功能时,出现调用的不确定性。

3,对于继承体系,应该怎么学习?★★★★★

                   参阅顶层类,使用底层类。

4.使用关键字:extends

5.作用:代码重用。为多态提供了前提;

6.this和super的区别:

                   1).this:

                            1).在任何类中使用;

                            2).存储的是本类对象的引用;

                            3).可以访问本对象的成员变量、成员方法、构造方法;

                   2).super:

                            1).在子类中使用;

                            2).存储的是父类对象的引用;

                            3).可以访问父类对象的成员变量、成员方法、构造方法;

7.类的初始化过程:

                   加载class文件

                   堆中开辟空间

                   变量的默认初始化

                   变量的显示初始化

                   构造代码块初始化

                   构造方法初始化

           成员变量-->构造代码块-->构造方法

8.Java中继承的特点:

                   1).Java中只能单继承;

                   2).Java中可以多级继承;

9.继承的好处和弊端:

                   好处:

                   1).代码复用

                   2).为多态提供了前提;

                   弊端:

                   1).由于继承,子类对父类产生了依赖;

1.2.2 继承的代码体现
/*
//描述学生。
class Student 
{
	//属性。
	String name;
	int age;

	//行为。
	void study()
	{
		System.out.println("good good study");
	}
}

//描述工人。
class Worker
{
	//属性。
	String name;
	int age;

	//行为
	void work()
	{
		System.out.println("hard work");
	}
}
*/

/*
为了提高复用,只建立一份代码。
一个类只要和另一个类产生关系就可以了
关系:继承。
发现了获取到所需内容的同时也获取到不该具备的内容。
为什么?
发现原来这个两个类之间根本就不存在继承关系。


怎么解决呢?
找到学生和工人的共性类型。将需要提供复用的代码进行抽取。
定义到一个共性类型的类中。
Person name age。

怎么在代码体现中让学生和Person产生关系呢?
只要通过关键字 extends(继承) 就哦了。 

*/

class Person
{
	String name;
	int age;

}

class Student extends Person//学生继承了Person 学生就是子类 Person就是父类(基类,超类)
{
	void study()
	{
		System.out.println("good good study");
	}
}
class Worker extends Person
{
	void work()
	{
		System.out.println("hard work");
	}
}
/*
面向对象 另一个特征:继承。
好处:提高了代码的复用性。让类与类产生了关系,给另一个特征 多态 提供了前提。

什么时候定义继承?
必须保证类与类之间有所属(is a)关系。 xxx是zzz中的一种。
苹果是水果中一种。狗是犬科中一种。

在Java中继承的体现:
Java允许单继承。不直接支持多继承,将多继承进行其他方式的体现。
单继承:一个子类只能有一个父类。
多继承:一个子类可以有多个父类。

*/

1.3抽象

1.3.1抽象特点

1,抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。

2,抽象类不可以创建实例,原因:调用抽象方法没有意义。

3,只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。

否则该子类还是一个抽象类。

1.3.2抽象类的细节

1,抽象类一定是个父类? 

         是的,因为不断抽取而来的。

2,抽象类是否有构造函数?

         有,虽然不能给自己的对象初始化,但是可以给自己的子类对象初始化。

         抽象类和一般类的异同点:

         相同:

                   1,它们都是用来描述事物的。

                   2,它们之中都可以定义属性和行为。

         不同:

                   1,一般类可以具体的描述事物。

                            抽象类描述事物的信息不具体

                   2,抽象类中可以多定义一个成员:抽象函数。

                   3,一般类可以创建对象,而抽象类不能创建对象。

3,抽象类中是否可以不定义抽象方法。

         是可以的,那这个抽象类的存在到底有什么意义呢?仅仅是不让该类创建对象。

4.使用abstract关键字修饰;可以修饰“类”,可以修饰“成员方法”;

                            abstractclass A{

                                     abstractvoid show();

                            }

         5.“抽象类”的特点:

                            1).不能被实例化,只能用于被继承;

                            2).可以包含:成员变量、构造方法、成员方法、抽象方法;

                            3).可以不包含抽象方法;

         6“抽象方法”的特点:

                            1).没有方法体;abstract void show();

                            2).必须被子类重写。除非子类也是个抽象类;

         7子类继承抽象类使用关键字:extends,仍然是单继承;

         8.一个子类继承了一个抽象类,必须实现抽象类中所有的抽象方法;

                     否则子类也必须是抽象的。

                            例如:

                                     abstractclass A{

                                               abstractvoid show();

                                     }

                                     classB extends A{  }       //编译错误。类B不是抽象的,继承类抽象类必须重写抽象类中的所有抽象方法。

                                     abstractclass B extends A{  }        //编译通过。类B没有重写父类中的抽象方法,但类B是抽象的。

         9.abstract关键字不能和哪些关键字共存:

                            1.private:抽象方法就是用来被子类重写的,而私有方法不能被子类重写;

                            2.final:抽象类和抽象方法就是用来被子类继承和重写的,而fianl类和final方法不能

                                 被继承和重写;

                            3.static:static修饰的方法在没有任何对象的情况下就会被分配内存空间;而抽象方法

                                  没有方法体,无法分配空间;

1.3.3抽象类代码体现

/*

需求:公司中程序员有姓名,工号,薪水,工作内容。

项目经理除了有姓名,工号,薪水,还有奖金,工作内容。

对给出需求进行数据建模。

在问题领域中先找寻其中涉及的对象。

程序员

         属性:姓名,工号,薪水

         行为:工作

项目经理

         属性:姓名,工号,薪水,奖金

         行为:工作

这些对象是否有关系呢?因为发现了他们之间的共性,应该存在着关系。

可以将他们的共性向上抽取到共性类型:员工。

员工:

         属性:姓名,工号,薪水

         行为:工作

发现员工的工作内容本身就不具体。应该是抽象的,由具体的子类来体现的。

一定要动手!

*/

abstract class Employee
{
	private String name;
	private String id;
	private double pay;
	/**
	构造一个员工对象,一初始化就具备着三个属性。
	*/
	public Employee(String name,String id,double pay)
	{
		this.name = name;
		this.id = id;
		this.pay = pay;
	}
	/**
	工作行为。
	*/
	public abstract void work();
}

//具体的子类:程序员。
class Programmer extends Employee
{
	public Programmer(String name,String id,double pay)
	{
		super(name,id,pay);
	}
	public void work()
	{
		System.out.println("code....");
	}
}

//具体的子类:经理。
class Manager extends Employee
{
	//特有属性。
	private double bonus;
	public Manager(String name,String id,double pay,double bonus)
	{
		super(name,id,pay);
		this.bonus = bonus;
	}
	public void work()
	{
		System.out.println("manage");
	}
}

class AbstractTest 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}

1.4多态

1.4.1多态的特点

   体现:父类或者接口的引用指向了自己的子类对象。

         好处:提高程序扩展性。

         弊端:不能使用子类对象的特有内容。

         前提:1,必须有关系(继承,实现);2,覆盖。

         转型:

                   向上转型:提高扩展性,隐藏子类型,不需要使用子类型的特有方法。               

                   向下转型:需要使用子类型的特有内容,注意:一定要instanceof判断类型,避免ClassCastExceptio

         记住:转型中,自始至终都是子类对象做着类型的变化。

         多态调用中,成员的特点:

                   成员变量,成员函数,静态函数。

                   总结:

                            对于成员变量和静态函数,编译和运行都看左边。

                            对于成员函数,编译看左边,运行看右边。原因是函数有覆盖,而是是动态绑                          定到当前对象上。

1.4.2多态代码
//多态

class Dog extends Animal 
{
	public void eat()
	{
		System.out.println("骨头");
	}
	public void lookHome()
	{
		System.out.println("看家");
	}
}

//描述猫
class Cat extends Animal 
{
	public void eat()
	{
		System.out.println("鱼");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}
//进行抽取。将共性的功能抽取到父类Animal中。
abstract class Animal
{
	public abstract void eat();
}

/*

多态:

         【体现】

                   父类的引用或者接口的引用指向了自己的子类对象。

                   Dogd = new Dog();//Dog对象的类型是Dog类型。

                   Animala = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。

         【好处】

                   提高了程序的扩展性。

         【弊端】

                   通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

         【前提】

                   1,必须有关系:继承,实现。

                   2,通常都有重写操作。

         【子类的特有方法如何调用呢?】

         Animala = new Dog();//Animal是父类型,new Dog()是子对象。

         但是父类型引用指向子类对象时,这就是让子类对象进行了类型的提升(向上转型)。

         向上转型好处:提高了扩展性,隐藏了子类型。弊端:不能使用子类型的特有方法。

         如果要想使用子类的特有方法,只有子类型可以用。

         可以向下转型,强制转换。

         Animala = new Dog();

         a.eat();

         Dogd = (Dog)a;//将a转型为Dog类型。向下转型。

         d.lookHome();

         向下转型什么时候用?当需要使用子类型的特有内容时。

         注意:无论向上还是向下转型,最终都是子类对象做着类型的变化。

         【向下转型的注意事项】

         Animala = new Dog();

         //Catc = (Cat)a;向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。

         所以为了避免这个问题,需要在向下转型前,做类型的判断。

         判断类型用的是关键字 instanceof

         if(ainstanceof Cat)//a指向的对象的类型是Cat类型。

         {

                   //将a转型Cat类型。

                   Catc = (Cat)a;

                   c.catchMouse();

         }

         elseif(a instanceof Dog)

         {

                   Dogd = (Dog)a;

                   d.lookHome();

         }

         【转型总结】

         1,什么时候使用向上转型呢?

                   提高程序的扩展性,不关系子类型(子类型被隐藏)。

                   需要用子类的特有方法吗?不需要,哦了。向上转型。

         2,什么时候使用向下转型呢?

                   需要使用子类型的特有方法时。

                   但是一定要使用 instanceof 进行类型的判断。避免发生ClassCastException

*/

class DuoTaiDemo2
{
	public static void main(String[] args) 
	{
		Dog d = new Dog();
//		d.eat();
//		d.lookHome();

		/*
		Animal a = new Dog();
		a.eat();//可以的。
//		a.lookHome();//不可以的。
		*/

		method(d);
		Cat c = new Cat();
		method(c);
	}
	public static void method(Animal a)
	{
		
		a.eat();
//		Dog d = (Dog)a;//ClassCastException:类型转换异常。
//		d.lookHome();

//		a.lookHome();//不可以,因为动物不具备这个功能。
	}

}

1.4.3多态成员的特点

/*

多态中,成员调用的特点。

1,成员变量。

         当子父类中出现同名的成员变量时。

                   多态调用该变量时:

                            编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。

                            运行时期:也是调用引用型变量所属的类中的成员变量。

                   简单记:编译和运行都参考等号的左边。

                                     编译运行看左边。

2,成员函数。

         编译,参考左边,如果没有,编译失败。

         运行,参考右边的对象所属的类。

                   编译看左边,运行看右边。

         对于成员函数是动态绑定到对象上。

3,静态函数。

         编译和运行都参考左边。

         静态函数是静态的绑定到类上。

【结论】

对于成员变量和静态函数,编译和运行都看左边。

对于成员函数,编译看左边,运行看右边。

*/

class Fu
{
	int num = 3;
	
	void show()
	{
		System.out.println("fu show run");
	}
	static void method()
	{
		System.out.println("fu static method run");
	}
}
class Zi extends Fu
{
	int num = 5;

	void show()
	{
		System.out.println("zi show run..");
	}
	static void method()
	{
		System.out.println("zi static method run");
	}
}



class DuoTaiDemo3 
{
	public static void main(String[] args) 
	{
		/*
		//测试成员变量的多态调用。
		Fu f = new Zi();
		System.out.println(f.num);//3
		Zi z = new Zi();
		System.out.println(z.num);//5
		*/
		/*
		//测试成员函数的多态调用。
		Fu f = new Zi();
		f.show();
		*/
		//测试静态函数的多态调用。
		Fu f = new Zi();
		f.method();
		//注意:真正开发静态方法是不会被多态调用的,因为静态方法不所属于对象,而是所属于类。
		Fu.method();
		Zi.method();
	}
}

/*
阶段一需求:笔记本电脑运行。
按照面向对象的思想,用代码体现。
名称提炼法。
笔记本电脑。
	行为:运行。

class NoteBook
{
	//运行功能。
	public void run()
	{
		System.out.println("notebook run");
	}
}

阶段二需求:想要在笔记本电脑上加上一个手握式鼠标。
多了个对象:鼠标。
		行为:开启,关闭。
class Mouse
{
	public void open()
	{
		System.out.println("mouse open");
	}
	public void close()
	{
		System.out.println("mouse close");
	}
}
笔记本怎么用鼠标呢?
在笔记本中多一个使用鼠标的功能。
需要修改原来的笔记本类中的内容,添加一个功能。
class NoteBook
{
	//运行功能。
	public void run()
	{
		System.out.println("notebook run");
	}
	// 使用鼠标功能。
	public void useMouse(Mouse m)
	{
		if(m!=null)
		{
			m.open();
			m.close();
		}
	}
}
//问题:如果想要加入一个键盘呢?
只要描述一个键盘类,并在电脑类中加入一个使用键盘的功能就哦了。
但是发现从鼠标开始这个问题就已经产生了,一旦需要添加新设备的时候,
都需要改变电脑的源码。这个扩展性是非常差的。

设计上该如何改进呢?
之前的问题在于外围设备的增加和笔记本电脑之间的耦合性过高。
如何降低外围设备和笔记本电脑的耦合性呢?
外围设备还不确定,我们不要面对外围具体设备。
为了让笔记本可以使用这些设备,可以事先定义好一些规则,
笔记本只要使用这些规则就可以了。
有了这些规则就可以进行笔记本的功能扩展。
后期这些外围设备只要符合这些规则就可以被笔记本使用了。

那么规则在java中该如何体现呢?接口。

//1,描述接口。USB。

//2,描述笔记本电脑:运行功能,使用USB接口的功能。



*/
//USB接口定义。
interface USB
{
	void open();
	void close();
}

//描述笔记本电脑。 
class NoteBook
{
	public void run()
	{
		System.out.println("notebook run");
	}

	//使用usb接口的功能。
	public void useUSB(USB usb)//接口类型的变量。接口类型的变量指向自己的子类对象。
								//USB usb = new Mouse();
	{
		if(usb!=null)
		{
			usb.open();
			usb.close();
		}
	}
}
//需要鼠标 。想要被笔记本电脑使用,该鼠标必须符合规则。
//描述鼠标。
class Mouse implements USB
{
	public void open()
	{
		System.out.println("mouse open");
	}
	public void close()
	{
		System.out.println("mouse close");
	}
}

class KeyBoard implements USB
{
	public void open()
	{
		System.out.println("KeyBoard open");
	}
	public void close()
	{
		System.out.println("KeyBoard close");
	}
}

/*
发现,接口的出现,
1,扩展了笔记本电脑功能。
2,定义了规则。
3,降低了笔记本电脑和外围设备之间的耦合性。
*/

class DuoTaiTest2 
{
	public static void main(String[] args) 
	{
		NoteBook book = new NoteBook();
		book.run();
		book.useUSB(null);
		book.useUSB(new Mouse());
		book.useUSB(new KeyBoard());
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值