面向对象(进阶)

1.理解封装的好处和JavaBean的规范

封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式;

封装好处:

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

提高了代码的复用性

提高安全性

 

JavaBean 是一种JAVA语言写成的可重用组件,它是一个类。

JavaBean规范:

  1. JavaBean 类必须是一个公共类, 即将其访问属性设置为 public, 如: public class Student{…}
  2. JavaBean 类必须有一个空的构造函数: 类中必须有一个无参的public构造方法
  3. 一个JavaBean类不应有公共实例变量, 类变量都为private, 如: private int age;
  4. 属性应该由一组读写方法(getXxx 和 setXxx)来访问,一般是IDE(Eclipse、IntelliJ IDEA)为属性生成getter/setter 方法

JavaBean属性一般以小写字母开头, 使用驼峰命名格式. 相应的 getter/setter 方法是 get/set 接首字母大写的属性名. 例如: 属性名为age, 其对应的getter/setter 方法是 getAge/setAge.

 

2.java四种访问权限

1、public: 所修饰的类、变量、方法,在内外包均具有访问权限;

2、protected: 这种权限是为继承而设计的,protected所修饰的成员,对所有子类是可访问的,但只对同包的类是可访问的,对外包的非子类是不可以访问;

3、包访问权限(default): 只对同包的类具有访问的权限,外包的所有类都不能访问;

4、private: 私有的权限,只对本类的方法可以使用;

面向对象(进阶)_第1张图片

3.理解继承的思想和好处

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为;

子类继承父类当中的属性和方法,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码);

需要注意的是 Java 不支持多继承,但支持多重继承。

 

4.掌握重写的意义和规范,区分重写和重载

重写:

重写发生在父类与子类之间,子类覆盖父类中的一个方法并对其进行重写,用以达到不同的作用。

在使用重写时,应该要注意:

(1)子类中的重写方法要和父类被重写方法有相同的返回值类型、方法名、参数列表和抛出的异常。

(2)子类不能重写父类中private方法,否则只是子类定义了一个新方法,不属于重写的范畴。

 

重载:

重载是指在一个类中定义了多个同名的方法,他们有着不同的参数列表(包括参数类型、参数个数、参数顺序等)。

在使用重载时,应该要注意:

(1)参数类型、个数、顺序一致,只是参数名称不一致,不能构成重载。

(2)不能通过方法的访问权限、返回值类型和抛出的异常类型来决定重载。

区别:

(1)重载发生在一个类里,被重载方法与重载方法是垂直关系。而重写发生在父类与子类中,被重写方法与重写方法是水平关系。

(2)具体调用哪一个重载方法是由参数列表来决定的。而具体调用哪一个重写方法是由调用对象的类型来决定的。

(3)在编译时就可以依据参数列表的不同决定调用哪个重载方法,属于编译时多态。

(4)只有在运行时才能确定调用哪一个重写方法,属于运行时多态。

 

5.掌握super关键字的使用,以及super和this的区别?

super关键字的三种用法:

1.在之类成员中,访问父类的成员变量;

2.在子类的成员方法中,访问父类的成员方法;

3.在之类的构造方法中,访问父类的构造方法;

this关键字的三种用法:

1.this调用本类中的属性,也就是类中的成员变量; 2.this调用本类中的其他方法; 3.this调用本类中的其他构造方法,调用时要放在构造方法的首行

this和super的区别:

1、super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内调用其它方法。

2、super()和this()均需放在构造方法内第一行。

3、尽管可以用this调用一个构造器,但却不能调用两个。

4、this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造 函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

5、this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。

6、从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

 

6. 了解继承和组合所表述的关系,合理的使用继承和组合

继承:

优点:

(1) 子类自动继承父类接口,在多态时很方便

(2) 创建子类时无需创建父类对象

缺点:

(1) 继承破坏封装性

给父类增加了一个方法A,这时子类与父类之间就可能越来越脱离is-a

举个例子:比如,鸟类有羽毛等属性,这里有一个需求是,定义一个有羽毛的鸡类,采用继承的方法很优雅也很方便,直接一个extends 就可以实现,但是如果有一天,这个鸟类添加了一个飞翔的公有方法,此前继承了鸟类的鸡类会自动继承了这个方法,鸡会飞翔?顶多就是矮距离飞跃。此时给鸡飞的方法就是破坏了鸡的封装性,鸡不应该有此方法。此时的鸡已经和有飞翔行为的鸟类之间不是is-a 关系了。

(2) 继承是紧耦合:

继承紧耦合体现在父类变就会影响子类,此时子类如果因此需要修改,重构的难度可能会很高。

(3) 子类对父类的扩展往往会增加系统结构复杂度

继承树深度加深,结构越复杂。

(4) 不支持在运行时指定父类

(5) 子类不能改变父类的接口

什么时候使用继承?

  1. 类之间很明显是一种is-a 关系,而不是has-a或者contain-a关系。
  2. 考虑多态时使用继承

面向对象(进阶)_第2张图片

组合:

优点

  1. 组合不破坏封装,相对于继承
  2. 组合松耦合,包装类和被包装类彼此独立,不会因为被包装类突然加个方法就使得包装类多了一个方法,包装类视情况包装所需方法。
  3. 支持动态组合,组合的方式在运行时可以根据条件来选择所组合的类。
  4. 包装类可以通过包装改变被包装类的接口,比如被包装类是实现了Set接口的,我可以通过包装,让包装类实现Map接口。

缺点

  1. 不能实现多态
  2. 无法自动获得被包装类的接口,比如被包装类实现了Set接口,包装类并没有自动获得此接口,需要经过包装,才有可能和他一样的接口。

什么时候使用组合:

  1. 子类只需要继承父类的一部分,继承就没辙了。
  2. 如果只是为了具有父类的一些属性方法,比如汽车具有轮胎和发动引擎,但是如果为此继承这两个类是很不明智的,使用组合更为恰当。
  3. 如果设计的子类是为了复用代码,并不是为了扩展父类,那么最好是选组合的方式,因为父类改变会影响子类。对于只是为了复用而继承的类很不利。

面向对象(进阶)_第3张图片

另外:组合优于继承是面向对象设计原则之一

 

7. 掌握多态的原理、发生多态的条件和多态的使用场合

它是指在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,这使得同一个属性或方法在父类及其各个子类中具有不同的含义。

前提:

1.继承:在多态中必须存在有继承关系的子类和父类。

2.重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。

3.向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法

好处:

  1. 减耦合
  2. 增强可以替换性
  3. 可扩展性
  4. 灵活性等...

案例:

/** * 支付抽象类或者接口 */ public abstract class Pay { abstract public String pay(); } /** * 支付宝支付 */ public class AliPay extends Pay { @Override public String pay() { System.out.println("支付宝pay"); return "success"; } } /** * 微信支付 */ public class WeixinPay extends Pay { @Override public String pay() { System.out.println("微信Pay"); return "success"; } } /** * 银联支付 */ public class YinlianPay extends Pay { @Override public String pay() { System.out.println("银联支付"); return "success"; } } 测试支付 public static void main(String[] args) { /** * 测试支付宝支付多态应用 */ Pay pay = new AliPay(); pay.pay(); /** * 测试微信支付多态应用 */ pay = new WeixinPay(); pay.pay(); /** * 测试银联支付多态应用 */ pay = new YinlianPay(); pay.pay(); } ------>输出结果如下: 支付宝pay 微信Pay 银联支付

 

8. 掌握向下转型和向上转型,以及instanceof运算符的使用

要转型,首先要有继承,子类继承了父类中的非私有属性和可以继承的方法,然后子类可以继续扩展自己的属性及方法。

向上转型:子类对象转为父类,父类可以是接口。公式:Father f = new Son();Father是父类或接口,son是子类。

向下转型:父类对象转为子类。公式:Son s = (Son)f;

1.向上转型

假设有一个Fruit类,Fruit类中有一个show()方法,代码如下:

class Fruit{

public void show() {

System.out.println("this is a fruit");

}

}

有一个Apple类继承自Fruit类,该类有自己的方法test(),并且重写了父类的show()方法,代码如下:

class Apple extends Fruit{

@Override

public void show() {

System.out.println("this is a apple");

}

public void test() {

System.out.println("i am a apple");

}

}

实例化Apple类,并新建一个Fruit类的引用变量引用该实例,调用实例的show()方法:

Fruit fruit = new Apple();

fruit.show();

这里用到了向上转型,换言之,就是用父类的引用变量去引用子类的实例,这是允许的。当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。

2.向下转型

并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。

实例化Apple类,并新建一个Fruit类的引用变量“fruit”引用该实例,然后新建一个Apple类的引用变量,引用向下转型的“fruit”变量,代码如下:

Fruit fruit = new Apple();

Apple apple = (Apple) fruit;

上述代码是允许的,因为fruit引用的对象原本就是Apple对象向上转型得到的,在对fruit向下转型后得到的还是Apple类的对象,能够被Apple类的引用变量引用。

假设有一个Orange类继承自Fruit类,代码如下:

class Orange extends Fruit{

@Override

public void show() {

System.out.println("this is a Orange");

}

public void test() {

System.out.println("i am a Orange");

}

}

实例化Apple类,并新建一个Fruit类的引用变量“fruit”引用该实例,然后新建一个Orange类的引用变量,引用向下转型的“fruit”变量,代码如下:

Fruit fruit = new Apple();

Orange orange = (Orange) fruit;

在运行的时候会报错,因为fruit对象是由Apple对象向上转型得到的,只能够向下转型成Apple对象,不能够向下转型成Orange对象。

好处:

可以减少编程代码

public static void run(Fruit fruit) {

fruit.show();

}

在main()方法中的代码如下:

public static void main(String[] args) {

run(new Fruit());

run(new Apple());

run(new Orange());

}

调用run()方法时的参数不仅是Fruit对象,也可以是Apple对象和Orange对象,当传入的是Apple对象和Orange对象时,就会向上转型成Fruit对象,但是调用的show()方法还是Apple对象和Orange对象的show()方法。这样就不需要在主类中同时重载三个run()方法,减少了代码量。

instanceof:

是Java中的二元运算符,类似于 ==,>,< 等操作符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。

 

9. 掌握final关键字的使用和Object类中的常见方法

final:在java中,final的含义在不同的场景下有细微的差别,但总体上来说,它指的是“这是不可变的”

用法:

1.修饰数据;用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以改变它的值,不会进行修改;

2.修饰方法参数;当用final修饰参数时表示,在方法内不在改变参数值;

3.修饰方法;用final关键字修饰方法,它表示该方法不能被覆盖;无法被重写;(谨慎使用)

4.修饰类;表示该类无法被继承;(谨慎使用)

Object类中的常见方法:

Object 是所有类的“老祖宗”。Java 中所有的类都有一个共同的祖先 Object 类,子类都会继承所有 Object 类中的 public 方法。

1.public final native Class getClass();

final 方法、获取对象的运行时 class 对象,class 对象就是描述对象所属类的对象。这个方法通常是和 Java 反射机制搭配使用的。

2.public native int hashCode();

该方法主要用于获取对象的散列值。Object 中该方法默认返回的是对象的堆内存地址。

3.public boolean equals(Object obj) {return (this == obj);}

该方法用于比较两个对象,如果这两个对象引用指向的是同一个对象,那么返回 true,否则返回 false。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一般都要重写这个方法。

4.protected native Object clone() throws CloneNotSupportedException;

该方法是保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。

5.String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}

返回一个 String 对象,一般子类都有覆盖。默认返回格式如下:对象的 class 名称 + @ + hashCode 的十六进制字符串。

6.public final native void notify();

final 方法,主要用于唤醒在该对象上等待的某个线程。

7.public final native void notifyAll();

final 方法,主要用于唤醒在该对象上等待的所有线程。

8.public final native void wait(long timeout) throws InterruptedException;

wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。

9.public final void wait(long timeout, int nanos) throws InterruptedException

该方法导致当前线程等待,直到其他线程调用此对象的 notify() 方法或notifyAll()方法,或在指定已经过去的时间

10.public final void wait() throws InterruptedException {wait(0);}

可以看到 wait() 方法实际上调用的是 wait(long timeout) 方法,只不过 timeout 为 0,即不等待。

11.protected void finalize() throws Throwable {}

该方法是保护方法,主要用于在 GC 的时候再次被调用,如果我们实现了这个方法,对象可能在这个方法中再次复活,从而避免被 GC 回收。

 

10. 掌握抽象类和抽象方法的特点和使用

//抽象类

abstract class Person {

String name;

public Person(){}//抽象类的构造方法

public abstract void dink();//抽象方法,无{}方法体

public void eat(){ //非抽象方法

};

}

抽象类:

使用abstract关键字声明的类

抽象类的特征:

 ( 1 ) 不可被实例化

(2)抽象类是有构造器的(所有类都有构造器)

(3)抽象方法所在的类,一定是抽象类(因为抽象方法是没有方法体的,如果所在的类不是抽象类,那么该类可以实例化对象,调用抽象方法,然后无方法体去具体实现功能,则矛盾)

(4)抽象类可以没有抽象方法的

抽象方法:

abstract修饰的方法为抽象方法;

抽象方法的特征:

(1)格式,没有方法体,包括{ },例如  public abstract void dink();

 (2)抽象方法只保留方法的功能,具体的执行,交给继承抽象类的子类,由子类重写改抽象方法

 (3)如果子类继承抽象类,并重写了父类的所有的抽象方法,则此子类不是抽象类,可以实例化的

 (4)如果子类继承抽象类,没有重写父类中所有的抽象方法,意味着子类中还有抽象方法,那么此子类必须必须声明为抽象的。

 

11. 掌握接口的定义和实现,以及接口和抽象类的区别

接口:

使用interface来定义一个接口。接口定义同类的定义类似,也是分为接口的声明和接口体,其中接口体由常量定义和方法定义两部分组成

格式:

[修饰符] interface 接口名 [extends 父接口名列表]{

[public] [static] [final] 常量;

[public] [abstract] 方法;

}

在类的继承中,只能做单重继承,而实现接口时,一次则可以实现多个接口,每个接口间使用逗号“,”分隔。这时就可能出现常量或方法名冲突的情况,解决该问题时,如果常量冲突,则需要明确指定常量的接口,这可以通过“接口名.常量”实现。如果出现方法冲突时,则只要实现一个方法就可以了。

接口和抽象类的区别:

相同点:

(1)都不能被实例化

(2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点:

(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。

(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以 间接地实现多重继承。

(3)接口强调特定功能的实现,而抽象类强调所属关系。

(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

 

12. 了解内部类的定义和特点

定义:

将一个类定义放到另一个类的内部,这就是内部类

面向对象(进阶)_第4张图片

 

13. 了解Java的内存管理和垃圾回收机制

JVM内存组成结构:

堆:所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Space和To Space组成

栈:每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果

本地方法栈:用于支持native方法的执行,存储了每个native方法调用的状态

方法区:存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可通过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值

垃圾回收机制:

JVM分别对新生代和旧生代采用不同的垃圾回收机制

新生代的GC:

新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和From Space或To Space之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代

JVM执行机制:

1.串行GC

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定

2.并行回收GC

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数

3.并行GC

与旧生代的并发GC配合使用

旧生代的GC:

旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行GC(Serial MSC)、并行GC(parallel MSC)和并发GC(CMS),具体算法细节还有待进一步深入研究。

面向对象(进阶)_第5张图片

你可能感兴趣的:(Java)