一、封装
(1)为什么会有封装?
public class User{
int age;
}
public class Test{
public static void main(String [] args){
User a = new User();
a.age=-100;
System.out.println(a.age);
}
}
这段代码虽然编译通过了,但是违背了现实生活的原理,怎么可能会直接访问随便访问一个的年龄呢?如果我把年龄改成-100呢,所以这个时候就有了封装。
(2)封装的代码实现两步:
第一步:属性私有化
第二步:1个属性对外提供两个set和get方法,外部程序只能通过set方法修改,只能通过set
可以在set方法中设立关卡来保证数据的安全性。
public class User{
private int age;
}
public class Test{
public static void main(String [] args){
User a = new User();
System.out.println(a.age);
}
}
这个时候虽然加了private实现了User的封装,但是Test根本就访问不了User了,这样就太安全了,所以这个时候需要加入方法间接的方法了。可以在方法里面输入限定值,让访问者不可以把age设置成负数。
(3)set、get方法的语法规则
java开发规范中有要求,set方法和get方法要满足一下格式。
get方法的要求:
public 返回值类型 get+属性名手字母大写(无参数){
Return xxx;
}
set方法的要求:
Public void set+属性名首字母大写(有1个参数){
Xxx=参数;
}
(4)注意:set和get方法都是实例方法,不能带有static,因为static是类级别的,而实例变量是属于对象级别的,所以不能带有set和get方法。
二、继承(extends)
(1)什么是继承,有什么用?
//银行账户类
//账户的属性:账号、余额
//父类
class Account{
//属性
private String actno;
private double balance;
//构造方法
public Account(){
}
public Account(String actno, double balance){
this.actno = actno;
this.balance =balance;
}
//setter and getter
public void setActno(String actno){
this.actno = actno;
}
public String getActno(){
return actno;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return balance;
}
}
//其他类型的账户,信用卡账户
//账户、余额、信誉度
class CreditAccount extends Account{
//属性
private double credit;
//构造方法
public CreditAccount(){
}
//setter and getter方法
public void setCredit(double credit){
this.credit = credit;
}
public double getCredit(){
return credit;
}
}
//使用继承机制来解决代码复用问题
public class ExtendTest01{
public static void main(String [] args){
//创建普通账户
Account act = new Account();
act.setActno("11111");
act.setBalance(10000);
System.out.println(act.getActno() + "余额" +act.getBalance());
//创建信用账户
CreditAccount ca = new CreditAccount();
act.setActno("222222");
act.setBalance(-10000);
System.out.println(act.getActno() + "余额" +act.getBalance());
}
}
从以上代码看来CreditAccount继承了Account类,这样CreditAccount就可以间接的使用actbo、balance属性了。
继承的作用:(a)基本作用,子类继承父类代码可以得到复用。(b)重要作用:有了继承关系,才有了后期的方法覆盖和多态机制(c)继承的相关特性: class A{} class B extends A{}
(d)java只有单继承,没有多继承。但是可以间接继承
class X{
}
class Y extends X
{
}
class M extends X
{
}
class Z extends Y{
}
(e)java中规定,子类继承父类,除构造方法不能继承外,剩下都可以继承。但是私有的属性无法在子类中直接访问。(父类中praivate修饰的不能直接访问,可以通过间接的访问。
(f)java中的类没有显示的继承任何类,则默认继承object类,Object类是Java语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有object类型中所有的特征。
最顶点是Object
(g)继承也存在缺点,耦合度会变高。父类修改,子类受牵连。
(2)通过子类对象调用继承父类的方法
其实调用的是自己的方法,只要是继承了,然后就将父类继承过来的方法归为自己所有了。
public class Extends
{
public static void main(String [] args){
//实现继承
Cat c = new Cat();
c.move();
System.out.println(c.name);
}
}
class Animal
{
String name ="xiaobao";
public void move(){
System.out.println("小白在移动");
}
}
class Cat extends Animal
{
}
(3) 什么时候可以继承
(a)在实际开发工,满足什么条件的时候,可以使用继承呢?
凡是采用"is a " 能描述的,都可以继承。
Cat is animal;
猫是一个动物,
以后开发中有一个A类,有一个B类,A类和B类确实也有重复的代码,但是并不一定都适合继承。
(b)任何一个类,没有显示继承任何类,默认继承object
三、多态
(1)多态转型的类型:
(a)向上转型(自动类型转换):子-->父
(b)向下转型(强制类型转换,需要加强制类型转换符):父-->子
注意:
Java中允许转型,也允许向下转型。
无论是向上转型,还是向下转型,两种类型之间必须有继承关系,没有继承关系编译器报错。
基本数据类型说自动类型、强制类型。在引用类型说成向上转型和向下转型。
(2)什么是多态?
多种形态,多种状态。父类型的引用指向子类型的对象。
向上转型
Animal a2 = new Cat();
Animal a3 = new Bird();
a2.move();
a3.move();
java程序分为编译阶段和运行阶段,先来看编译阶段。(java是在编译的时候创建对象还是在运行的时候创建对象?)
对于编译器来说,编译阶段只知道a2的数据类型时Animal,所以编译器在检查语法的时候,会去Animal.class。
字节码文件中找move()方法,编译阶段还没有new对象,找到了,绑定Move()方法,编译通过,绑定成功(编一阶段属于静态绑定),其实这里的原理也就是,编译只是检查你的语法结构,语义,字节码之类的错误,并不会分配内存空间,也就不会创建对象。
再来看运行阶段:
运行阶段的时候,实际上在堆内存中创建的java对象是cat对象,所以Move的时候,真正参与Move的对象是一只猫,所以运行
阶段会动态执行cat对象的move()的方法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)
多态表示多种形态:编译的时候一种形态,运行的时候另一种形态。多态就是指,父类型的引用指向子类型的对象。
(3)向下转型
我现在在猫类中定义一个catchMouse的方法。
Animal a5 = new Cat(); //底层对象是一只猫
a5.catchMouse();
//分析一下这个程序能否编译和运行呢?
答案是不能。
因为编译器只知道a5的类型时Animal,去Animal.class文件中找catchMouse()方法
结果没有找到,所以静态绑定失败,编译报错,无法运行。
如果我现在就想抓老鼠了,怎么办?
这个时候就要向下转型了,必须用强制类型转换。
什么时候使用向下转型?
当你需要访问的是子类中特有的,那么就需要向下转型了。
Cat x = (Cat)a5;
x.catchMouse();
但是向下转型是有风险的。
Animal A6= new Bird();
//表面上 a6是一个Animal,运行的时候实际上是一只鸟儿。
Cat y = (Cat)a6;
y.catchMouse();
分析一下程序,编译报错还是运行报错?
编译没有问题,
运行阶段,堆内存实际创建的对象是:Bird对象。在实际运行过程中,拿着Bird对象转换成Cat对象
就不行了,因为Bird和Cat之间没有继承关系。
怎么避免ClassCastException异常的发生???为了避免向下转型风险,所以有了Instanceof
(4)运算符 Instanceof
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。
第二:instanceof的语法:
(引用 instanceof 类型)
第三: instanceof运算符的运算结果只能是:true/false
第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。
假设(c instanceof Cat)为true表示:
c引用指向的堆内存中的java对象是一个cat。
假设(c instanceof Cat)为false表示:
c引用指向的堆内存中的java对象不是一个cat。
四、多态在开发中的作用(超级重要)
(1)举例
public class More
{
public static void main(String[] args){
//创建主人对象
Moster xiaodong = new Moster();
//创建各种宠物对象
Dog d1 = new Dog();
d1.setName("xiaohei");
Cat c1 = new Cat();
c1.setName("xiaomao");
YingWu y1 = new YingWu();
y1.setName("xiaoying");
//对象喂养宠物
xiaodong.feed(d1);
xiaodong.feed(c1);
xiaodong.feed(y1);
}
}
class Moster
{
public void feed(Pet p){
System.out.println("喂养"+ p.getName());
}
}
class Pet
{ private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void eat(){
System.out.println("吃");
}
}
class Dog extends Pet
{
}
class Cat extends Pet
{
}
class YingWu extends Pet
{
}
这里体现了多态
class Moster
{
public void feed(Pet p){
System.out.println("喂养"+ p.getName());
}
}
如果没有运用这个多态,那么会使什么样子的?
public void feed(Dog d){
d.eat()
}
public void feed(Cat c){
c.eat();
}
如果运行多态,我们只有写一个方法就可以。
为了增强扩展类,所以面向父类型编程,不建议面向具体编程。
(2)多态在开发中的作用是:
降低程序的耦合度,提供程序的扩展力。
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类,这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
这里提到了一个软件开发原则:
OCP(对扩展开放,对修改关闭)
目的:降低程序耦合度,提高程序扩展力。
面向抽象编程,不建议面向具体编程。
总结:面向对象是Java基础最重要的了,同时,正是有了封装才能有继承,因为继承提供了对象,有了继承才有了多态。采用多态,继承和多态都有复用的功能。其实这里的多态,给我的感觉总有一种变量的感觉,其实,仔细想一下,它也是一种变量啊,Pet p,那不就是在栈中开辟了一块内存,Pet类型的变量嘛。所以,想要达到代码的扩展性,必须更好的利用多态以及变量。