Java面向对象特性总结
- 2019 年 10 月 3 日
- 筆記
1.面对对象与面对过程的区别
什么是封装?我看到过这样一个例子:
我要用洗衣机洗衣服,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内 部的结构吗?有必要碰电动机吗?有必要了解如何通电的吗?
如果是对于面向过程来说,这些你都得知道。“吾生也有涯,而知也无涯”,面向对象的封装与庄子的思想遥相呼应:用有限的时间去做更适合的事情。
面对过程是软件开发的先驱者,但其局限性不能满足当今的主流市场需求。
-
面对过程注重微观,每一步都亲力亲为;面对对象注重宏观,更加偏向于整体的流程。
-
性能:面对过程执行效率更高。Java是编译成字节码给JVM运行,而面向过程直接编译成机器码执行
-
复用性:面对对象的封装,继承极大的扩展了代码的复用性
2.面对对象特征——封装
封装的核心思想是隐藏实现,暴露接口。
封装的目的:
- 解耦(符合迪米特法则)
- 保护数据安全(通过getter方法来获取类中属性值)
- 访问权限修饰符
修饰符 | 权限范围
public | 本类,子类,本包,外部包
protected | 本类,子类,本包
default | 本类,子类
private | 本类
3.面对对象特征——继承
继承主要目的是实现代码复用。
特性:
- 子类具有父类非private的属性和方法
- 子类可以扩展自己的属性和方法
构造器的继承问题
- 构造器是不会被子类继承的,但子类的对象在初始化时会默认调用父类的无参构造器。
- 当父类显示写了有参构造器,且没有无参构造器。子类继承父类的时候必须显示的调用父类的有参构造器。调用的方式可以使用super(a,b)来调用。
static修饰符的继承问题
子类是不会继承父类被static修饰的方法和变量,但是可以调用。
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
- 方法不能重写
- 类不能继承
- final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只 能被赋值一次。
子类对象实例化过程
new出一个子类对象的时候,必须先new出一个父类对象。子类在调用构造方法时,会先调用父类的构造方法。(默认)
如果不写构造方法,编译器默认加上一个无参构造器。如果写了构造器,编译器不会添加。
如果写了有参构造器,子类就必须调用父类的构造方法。super(参数)。
如果同时有有参和无参构造器,那么会默认调用无参。也可以自定义调用有参。
4.面对对象特征——多态
提高了代码的通用性。
多态分为编译时多态(方法重载)和运行时多态(方法重写)。
- 重载与重写
重写指子类重写父类的方法,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private或者final 则子类就不能重写该方法。
重载发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
- 向上转型
在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。
-
强制转换
从子类到父类的类型转换可以自动进行
从父类到子类的类型转换必须通过造型(强制类型转换)实现
无继承关系的引用类型间的转换是非法的
5.Object类
- Object类是所有Java类的根父类
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类 为java.lang.Object类(任何类都可以调用Object的方法)
package java.lang; public class Object { private static native void registerNatives(); static { registerNatives(); } public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } protected native Object clone() throws CloneNotSupportedException; public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); } protected void finalize() throws Throwable { } }
Object的主要组成:
- public native int hashCode(); 取得hash码
- equals(Object obj) 比较对象
- clone() 可用于复杂对象的深拷贝
==与equals的区别
== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型 就是比较内存地址。
equals的话,,如果该方法没有被重写过默认等同于==;
但是很多类默认重写了:(比如String)
6.包装类的使用
装箱:基本数据类型—>包装类
拆箱:包装类—>基本数据类型
//装箱 Integer t = new Integer(500); //float to Float Float f = new Float(“4.56”); //拆箱 Integer x = 3; int i = x.intValue(); //int to String String str = String.valueOf(1); //String to int int x = Integer.parseInt(str1) ; //包装类 to String Integer x = 8; String str = x.toString(); or String str = Integer.toString(3);
7.static关键字
在Java类中,可用static修饰属性、方法、代码块、内部类。
被修饰后的成员具备以下特点:
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接用类名.属性或方法调用
在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
static修饰的方法不能被重写
static的应用:单例模式——静态内部类
class StaticClassInner { private StaticClassInner() {} /* 使用静态内部类,实现了延迟加载 调用getInstance()方法时,才会加载StaticClassInnerInstance。 通过JVM类加载线程安全的机制,避免了线程不安全。 */ private static class StaticClassInnerInstance { private static final StaticClassInner INSTANCE = new StaticClassInner(); } public static StaticClassInner getInstance() { return StaticClassInnerInstance.INSTANCE; } } public class Operation { public static void main(String[] args) { StaticClassInner doubleCheck1 = StaticClassInner.getInstance(); StaticClassInner doubleCheck2 = StaticClassInner.getInstance(); System.out.println(doubleCheck1.hashCode()); System.out.println(doubleCheck2.hashCode()); } }
8.代码块
对类或对象进行初始化。
代码块可分为静态代码块和非静态代码块。(有无static修饰)
静态代码块:用static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
- 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
- 静态代码块的执行要先于非静态代码块。
- 静态代码块随着类的加载而加载,且只执行一次。
非静态代码块:没有static修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
9.抽象类和抽象方法
- 用abstract关键字来修饰一个类,这个类叫做抽象类。
- 用abstract来修饰一个方法,该方法叫做抽象方法。
10.interface
- 用interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。
- 接口中的所有抽象方法都默认是由public abstract修饰的。
- 接口中没有构造器。
- 接口采用多继承机制。
定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }
接口和抽象类之间的对比
11.内部类
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者 称为外部类。
Inner class的名字不能与包含它的外部类类名相同;
成员内部类(static成员内部类和非static成员内部类)
class Outer { private int s; public class Inner { public void mb() { s = 100; System.out.println("在内部类Inner中s=" + s); } } public void ma() { Inner i = new Inner(); i.mb(); } } public class InnerTest { public static void main(String args[]) { Outer o = new Outer(); o.ma(); } }
局部内部类(不谈修饰符)
class 外部类{ 方法(){ class 局部内部类{ } } {class 局部内部类 { } } }
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方 都不能使用该类
- 但是它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类 的父类或父接口类型
- 局部内部类可以使用外部方法的局部变量,但是必须是final的。
匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一 个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或 实现一个类。
匿名内部类的特点
- 匿名内部类必须继承父类或实现接口
- 匿名内部类只能有一个对象
- 匿名内部类对象只能使用多态形式引用
interface A{ public abstract void fun1(); } public class Outer{ public static void main(String[] args) { new Outer().callInner(new A(){ //接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名 public void fun1() { System.out.println(“implement for fun1"); } });// 两步写成一步了 } public void callInner(A a) { a.fun1(); } }
12.异常处理
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。
(开发过程中的语法错误和逻辑错误不是异常)
Java程序在执行过程中所发生的异常事件可分为两类:
- Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源 耗尽等严重情况。一般不编写针对性 的代码进行处理。
- Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使 用针对性的代码进行处理。
异常处理机制一: try-catch-finally
在编写程序时,经常要在可能出现错误的地方加上检测的代码, 如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据 而是字符等。过多的if-else分支会导致程序的代码加长、臃肿, 可读性差。因此采用异常处理机制。
-
Java程序的执行过程中如出现异常,会生成一个异常类对象, 该异常对象将被提交给Java运行时系统,这个过程称为抛出 (throw)异常。
-
如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处 理。如果异常没有在调用者方法中处理,它继续被抛给这个调用 方法的上层方法。这个过程将一直继续下去,直到异常被处理。 这一过程称为捕(catch)异常。
-
程序员通常只能处理Exception,而对Error无能为力。
-
异常处理是通过try-catch-finally语句实现的。
try{
…… //可能产生异常的代码
}
catch( ExceptionName1 e ){
…… //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
…… //当产生ExceptionName2型异常时的处置措施
}
[ finally{
…… //无论是否发生异常,都无条件执行的语句
} ]
-
如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则 编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为 运行时异常。
异常处理机制二: throws
- 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这 种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理, 而由该方法的调用者负责处理。
- 在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可 以是方法中产生的异常类型,也可以是它的父类。
- 重写方法不能抛出比被重写方法范围更大的异常类型。