------<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());
}
}