Java SE 学习笔记03

面向对象基础

 

★面向对象强调数据结构第一,算法第二,而面向过程强调算法第一,数据结构第二


★类之间的关系:
依赖关系Dependence (”uses–a”):如果一个类的某个方法使用了另一个类的对象,我们称前者依赖后者,那么这两个类之间存在依赖关系。如Person类中有一个transport方法要求传入一个Car类的对象,即Person要使用Car运输,那么Person类就依赖于Car类
聚集关系Aggregation (”has–a”) :指的是一个类中包含有另一个类的对象,如Book类里面包含有Date类。聚集是通过把一个类定义为另一个类的属性来实现的
继承关系Inheritance (”is–a”):指的两个类存在继承关系,如Student类和Person类之间就是继承关系

 

★内聚(cohesion)和耦合(coupling)
内聚和耦合是OOD(Object Oriented Design)的两个概念。具备良好质量的OOD应该达到高内聚、低耦合的要求。高耦合、低内聚会降低软件系统的稳定性、可扩展性、可复用性
★内聚——一个类内部组成具有单一、明确目标的程度。高内聚是一种理想的状态,其中类成员支持单一、明确的角色或职责
★耦合——一个类和另一个类联系的紧密程度。低耦合是一种理想的状态,通过设计良好封装的类,把类与类之间的相互吸引减至最低,一个类内部的改变不会波及到其他类的变动

 

★封装(Encapsulation)
就是对属性和方法的载体类的访问只能通过其提供的接口(方法)来访问,把实现细节隐藏起来,也就是说,具体实现对程序员来说是透明的
★封装的好处在于对类内部的改变不会影响到其他代码
★封装的实现:将成员变量定义为private,而访问这些变量的方法定义为public
★封装是程序重用(reuse)和可靠 (reliability)的关键

 

★继承(Inheritance)
★当继承一个类的时候,不仅可以继承行为(方法),还可以继承数据(成员变量)。私有的数据成员和方法不能被继承
★JAVA中所有的类都是Object类的子类
★虽然子类继承了父类的成员变量,但是对于父类私有的成员变量,子类并不能直接访问

★对象(Objects):对象具有三个特征——行为、状态和特性
注意:Date date = new Date();这里的date不是对象,而只是对象的引用

★构造方法(Constructors)
★构造方法和类名相同
★构造方法在一个类中可以有一个或多个(即使不写构造方法,系统会有默认的无参构造方法,但如果自定义了带有参数的构造方法,系统不会再创建无参构造器)
★构造方法可以包含0个或多个参数
★构造方法没有返回类型
★构造方法只有new关键字能调用
★构造方法不能被继承
★构造方法可以抛出异常,如果父类的默认无参构造方法抛出了异常,那么子类的默认无参构造方法必须处理父类抛出的异常
★对于子类的构造方法,如果没有调用子类的其他构造方法,那么其必须调用父类的构造方法,默认会调用父类的无参构造方法,如果父类没有无参构造方法,那么子类必须显式调用父类的其他构造方法(super),否则编译出错
★构造方法的执行过程为:构造器直接调用其父类构造器,其父类的构造器再调用它的父类的构造器,直到调用Object类的构造器为止
★接口没有构造器,抽象类的构造器在其实现类实例化时被调用

★this关键字
★this关键字代表了当前对象,即this关键字引用的是当前创建的类实例对象的句柄。如this.方法名,this.属性名
★this关键字可以用在构造器中,表示调用该类的其他构造器,以避免代码重复。语法形式为this(...)。其必须在构造器的第一行。但是不能调用自身
★this关键字用于构造方法时,不能同时使用super关键字
★this关键字也可以用于调用本类的其他方法。this.方法名
★this关键字不能用于static方法内

 

★super关键字
★super关键字代表了父类对象,即super关键字引用的是当前创建的类实例对象的父类的句柄
★super关键字可以用在构造器中,表示调用父类的构造器(如果子类没有调用其他的构造器,也没有显式调用父类的构造器,其默认调用的是父类的无参构造器,如果父类没有无参构造,那么编译出错)
语法形式为super(...)。其必须位于构造器的第一行
★super关键字用于构造方法时,不能同时使用this关键字
★super关键字也可以用于调用父类的普通方法。super.方法名

 

★多态(polymorphism):指子类和父类具有同名的重写方法,并且允许父类引用指向子类对象,即允许把一个子类对象赋予一个父类的引用型变量,在运行期由系统自动判定应该调用子类的方法还是父类的方法。多态是运行期行为
动态绑定:Automatically selecting the appropriate method at runtime is called dynamic binding
动态绑定的执行过程:
编译器首先会找到子类与父类中所有的和被调用方法的方法签名相同的方法。(注意,父类中只找public的)
编译器会自动进行重载方法判断(如果存在重载方法)
JVM在运行时根据实际的对象类型调用其方法
静态绑定:If the method is private, static, final, or a constructor, then the compiler knows exactly which method to call. This is called static binding.

注意:多态性只存在于方法之上,如果父类对象的引用指向子类,调用方法时调用的是子类的重写方法,调用属性时调用的是父类的属性(指的是父类与子类存在同名的属性时,不过这样的可能性不大,因为只有属性为public时才能测试通过)
注意:当父类引用指向子类对象时,只能调用父类中已有的方法,不能调用子类中新添加的方法,虽然其实际类型是子类

这里有个经典的例子:假设有Employee类,其有子类Manager类
Manager[] managers = new Manager[2];
Employee[] employees = managers;
Employees[1] = new Employee();
编译通过,运行时会抛ArrayStroeException。因为多态是运行期行为。但这样是正确的:
Employee[] employees = new Employee[2];
Employees[0] = new Manager();
因为两个数组其实指向的都是同一个Manager数组,这个数组中存放的是Manager对象

★JavaBean组件
JavaBean组件是具有属性的JAVA类,其属性一般为private的。因为属性是私有的,因此从类外访问属性的唯一方法就是通过类提供的非私有方法
JavaBean组件中更改属性值的方法成为setter方法,获取属性的方法成为getter方法
命名原则:public void set+属性名首字母大写(参数)
public (set方法参数的类型) get+属性名首字母大写(参数)
布尔类型,可设置为is+属性名首字母大写
public是必须的
扩展:对于JAVA中Listener的命名规则:对于添加Listener addXXXListener(XXX监听器);对于删除Listener removeXXXListener(XXX监听器类型);

 

★abstract关键字
★abstract关键字只能修饰类和方法,不能修饰变量,因为该关键字表明的是未实现的含义,而属性不存在未实现
★abstract关键字修饰的方法为抽象方法,其不包含方法体
★abstract关键字修饰的类为抽象类。抽象类可以不包含抽象方法,但是含有抽象方法类一定要声明为抽象类,抽象类也可以包含非抽象方法
★子类继承抽象类,必须完全实现其所有的抽象方法,否则子类只能声明为抽象类。当一个类实现一个接口时,必须完全实现接口内所有的抽象方法,否则也必须声明为抽象类
★抽象类有构造方法,但是不能直接调用,否则编译出错。抽象类不能被实例化。根据多态性,抽象类可以指向其实现类
★抽象类不能被final修饰符修饰,因为抽象类需要子类继承实现,而final修饰的类是不能被继承的
★抽象方法不能被声明为final的,也不能被private修饰符修饰,因为抽象方法需要被子类继承而实现,而final修饰的方法和private修饰的方法是不能被继承的

 

★final关键字
★final关键字可以修饰类、属性、方法,表示该修饰的对象具有不可变性
★final修饰基本数据类型的变量为一个常量,其值是只读的。final修饰的变量必须在使用前被初始化,三种初始化方法:声明后直接赋值;构造方法中赋值;静态语句块中赋值(只针对static final)
★final修饰引用变量,表示该变量一旦分配给一个值就不能再改变,即该变量一旦引用了一个对象后,再也不能引用其他对象,更不能将其他对象的句柄赋值给它。但是可以改变final变量所引用对象的内容
★final修饰符位于方法前,表示该方法不能被子类重写,但是可以被重载
★final修饰符位于类前,表示该类不能被继承,声明为final的类,其所有的方法默认为final的,但是属性不是final的
★final修饰符是唯一可以用于方法局部变量的修饰符。
如果存在多个构造器,需要同时初始化final变量

★native关键字
★native关键字只能用于修饰方法。一个在方法名前加上native关键字的方法为本地方法,其目的是利用本地资源来扩展JAVA功能,与JAVA的机制无关
★本地方法如同抽象方法,没有方法体,因为对本地方法的实现,不是JAVA语言,而是采用依赖于本地平台的其他语言编写的代码来实现的
它们位于JVM之外,可以通过System.loadLibrary()方法载入,其通常放在静态代码块中来装入本地方法,如果System.loadLibrary()方法没有载入成功,会抛出UnsatisfiedLinkError
★Object类中的clone和notify方法都是本地方法
★本地方法可以被private修饰符修饰

★static关键字
★static关键字不能修饰顶层类,可以修饰内部类。可以修饰属性、方法、代码块。该修饰符表明所修饰的对象属于类范畴,而非类实例
★static关键字修饰的类属性有两种引用方式,类名.属性名;或者对象名.属性名。推荐使用前者,因为后者可读性不好
★类的静态属性被整个类所共享,因此,任何一个类实例改变了属性的值,所有类的实例拥有的这个属性值都会同步改变,其不能被序列化
★方法中声明的变量不能是静态的,因为方法中声明的变量是局部的
★static修饰的方法除了可以访问其内部定义的变量外,只能访问被static修饰符所修饰的静态变量,如果需要访问非静态的属性,则必须通过 对象.属性 的方式
★对于静态方法和非静态方法的调用方式是不同的。对于非静态方法的调用是在运行期决定的,而对静态方法的调用是在编译期决定的
★静态代码块主要用于初始化,该代码块只被执行一次,即首次装载类时被执行,如果存在多个静态代码块,那么按照出现的先后顺序被执行。相反,非静态代码块每次创建新对象时均会执行

辨析:静态方法和非静态方法
静态方法:可以直接访问本类的静态属性,其他的静态方法,可以在其内部声明非静态变量,但不能声明静态变量,静态方法中不能使用this和super关键字
非静态方法:不能声明静态变量,可以声明非静态变量,可以调用本类的所有方法(包括静态和非静态),可以调用本类的所有属性(包括静态和非静态)
经典代码:
public class HelloWorld{
static{
System.out.println("Hello, World");
System.exit(0);
}
}

 

★接口(interface)
★接口是一个纯的抽象类。接口的语法为interface interfaceName{}
★接口默认的声明为public abstract,因为其必须要由实现类实现
★接口中包含的方法全部为public abstract修饰的方法,其均没有方法体
★接口中定义的属性全部为public static final修饰的属性,其均为常量
★接口实现通过implements关键字。实现类必须重写接口中所有的方法,否则只能将接口的实现类必须声明为抽象类
★接口可以继承接口,通过extends关键字实现,且可以继承多个接口,逗号分开
★接口中的方法不能被static、native、strictfp、final关键字修饰。
★接口不能继承其他的类

注意:接口中的方法和常量可以有下面的声明方式(全部合法)
常量声明:(常量一旦声明不能重新赋值)
public static final int x = 1;
public int x = 1;
int x = 1;
static int x = 1;
final int x = 1;
public static int x = 1;
public final int x = 1;
static final int x = 1;
方法声明:
void method();
public void method1();
abstract void method1();
public abstract void method1();
abstract public void method1();

 

★方法重写(override)和方法重载(overload)
★重写:所谓方法重写是指父类中的一个方法在子类中获得重新定义,方法名、参数列表、返回类型均不改变
规则:
重写方法必须和被重写方法具有相同的方法名、参数列表和返回值类型
参数列表中要求数目、类型、顺序必须完全一致
只有继承存在的情况下才能重写方法
JAVASE5开始允许协变返回类型,即重写方法返回的类型是可以是被重写方法返回类型的子类
重写方法的访问控制修饰符的范围不能小于被重写方法
重写方法抛出的异常必须和被重写方法一致或是其子类,或者不抛出
注意:对于接口中的方法重写,其修饰符只能是public的
普通的成员方法的重写是一种多态行为,而静态方法实在类编译的时候就确定了,也就是说静态方法绑定到类,成员方法绑定到对象,即静态方法不能重写
对于父类私有的方法来说,子类中如果有同样的方法不会构成重写。(即私有方法不能被重写,因为重写依赖于继承,而私有方法不能被继承)

 

★重载:所谓方法重载是指一个类中定义多个方法名相同,参数列表不同的方法,所谓参数列表不同指的是参数的类型、数目、顺序不同(三者之间有一个符合即构成方法重载)
规则:
重载方法必须拥有不同的参数列表,数目、类型、排列顺序三者有一即可
重载方法在同一个类中定义
重载方法的返回类型、访问修饰符以及抛出的异常均不能作为重载的判断标准
重载方法之间可以相互调用(指的是参数类型相同、数目不同的重载方法)
static关键字不能作为方法重载判定的标准
是否抛出异常不能作为方法重载的判定标准

★访问权限修饰符(public、protected、default、private)[不考虑内部类]
★访问权限修饰符不能修饰局部变量,只有final可以修饰局部变量
★一个属性或类只能有一个访问权限修饰符
★public修饰符可以修饰类、属性、方法、构造方法
★public修饰符访问权限最大,其修饰的类、属性、方法可以在任意位置被访问
★protected修饰符可以修饰属性、方法、构造方法
★protected修饰符修饰的属性、方法只能被本类或本类的子类访问,即使子类在不同的包中(同一个包中的类也可以访问)
★default修饰符(默认没有修饰符)可以修饰类、属性、方法、构造方法、代码块
★default修饰符修饰的类、属性、方法等只能在本包中被访问
★private修饰符可以修饰属性、方法、构造方法
★private修饰的属性、方法只能被本类访问
注意:访问范围控制从类到成员,如果类不可见,其成员不管被任何访问修饰符所修饰,均不可见。即要确定成员的可见性,首先要确定类的可见性
如果类A和类B在不同的包中且访问修饰符均为default,假设A是B的父类,即使导入了A,编译依然不会通过。而且也不能声明返回类型为A的方法或A类型的变量(A和B无继承时也成立)
关于protected的奇怪之处:
package parent
public class Parent{
protected int x = 3;
}

package child
public class Child extends Parent{
public void test(){
System.out.println("x is " + x);--->正确
Parent parent = new Parent();--->正确
System.out.println("parent's x is" + p.x);-->不正确
}
}
结论:protected访问权限只允许子类跨包访问,在子类中不允许使用父类的引用调用父类中的protected修饰的变量或方法(但静态属性和方法可以被调用)
即子类可跨包访问,而父类遵守的是default访问权限(即从包外访问protected修饰的变量和方法只能通过继承实现)
此外,如果有一个类(Other)和子类处于同一个包中,其中有子类对象的引用,子类对象的引用也不能访问父类中protected修饰的变量或方法,这时的protected权限在子类中其实变成了private权限,因此类外是不能访问的
★protected = package + subclass

 

★Object类
★Object类是所有Java类的父类,如果一个类没有使用关键字extends继承一个类,其默认继承了Object类
★equals()方法。equals()方法默认执行的是==的比较,参看Object类的源码可知。因此要想实现自定义类的比较需要重写此方法自定义比较的规则。在API中有几个类已重写了equals()方法,如String、Date和封装类
equals()方法遵循一些原则:
自反性 如果x不为null,x.equals(x)为true
对称性 x.equals(y)为true,那y.equals(x)为true
传递性 x.equals(y)为true,y.equals(z)为true,那x.equals(z)返回值也为true
一致性 多次调用x.equals(y)结果均为true
非空性 如果x取值不为null,那么x.equals(null)为false
equals()方法的一般写法:(以 Person类为例)

public boolean equals(Object object){参数只能是Object类
if(this == object)return true;
if(object == null)return false;
if(this.getClass() != object.getClass())return false;
Person person = (Person)Object;
return ((this.name).equals(person.name)) && (this.age == person.age);
}
上面代码中的getClass()可以用instanceof测试。但不推荐使用instanceof。假设有一个Employee类和一个Manager类,后者继承自前者,分别有两个类的对象e和m,假设他们有相同的名字、薪水和入职日期。现在有e.equals(m),如果在equals方法中用(m instanceof Employee)来判断,肯定为true,但是根据equals对称性原则,要求m.equals(e)的返回结果为true,这时的equals方法中(e instanceof Manager)返回的要么是false要么抛出异常
如果子类有自己判断equals相等的原则,那么对称性原则要求强制使用getClass();
如果子类使用父类判断equals相等的原则,那么为了使不同子类之间的equals比较能够进行,可以使用instanceof来判定。instanceof superclass

public boolean equals(Person person)这个不叫方法重写。
数组的equals()和hashCode()方法:调用Arrays.equals(type[] a, type[] b)和Arrays.hashCode(type[] a);

★hashCode()方法,其主要作用是配合equals()方法来使用
重写equals()方法是必须重写hashCode()方法。因为两个对象如果用equals()方法比较相等的话,那么他们的hashCode()方法返回值也必须相等
如果两个对象的hashCode()方法返回值不相等,则两个对象的equals()方法比较返回false,即两个对象一定不相等
如果两个对象equal()方法返回值为false,则两个对象的hashCode()方法返回值并不一定要求一定不相等(即其hashCode可以相等)
hashCode()方法遵循原则:
多次调用x.hashCode()的返回值均是相等整数
hashCode()方法在重写是根据equals()方法比较的内容重写:
如Person的hashCode方法重写为:
public int hashCode(){
return 7 * name.hashCode() + 13 * new Integer(age).hashCode();
}

★toString()方法:返回的是该对象的字符串表示
只要对象和一个字符串通过 + 号进行连接的话,会自动调用该对象的toString()方法
★对于一维数组对象,调用的是Arrays.toString(arrayName)方法进行打印
★对于多维数组对象,调用的是Arrays.deepToString(arrayName)方法进行打印
子类中对于父类的equals()、hashCode()、toString()方法的重写是通过super调用父类中的equals()、hashCode()、toString()方法再加上子类自己添加的内容(这里假设父类已重写了Object的equals()hashCode()和toString()方法)

 

★clone()方法:protected修饰,用于拷贝对象,所有使用clone()方法的类都应该实现Cloneable接口(数组除外)
浅克隆:如果一个类中不包含引用类型的属性(如Date类型的属性),那么使用super.clone()方法可以实现该类对象的拷贝
深克隆:如果一个类中包含引用类型的属性,那么该类的每个引用类型的属性也必须分别调用clone()方法才能保证对该类对象实现正确的拷贝
假如有一个员工类:其有String类型的name属性和Date类型的hireDate属性。那么如何对其进行clone呢?首先Employee类必须实现Cloneable接口,然后重写里面的clone方法
public Employee clone() throws CloneNotSupportedException {
Employee cloned = new Employee();
cloned = (Employee) super.clone();
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
对于任何数组对象,其clone方法都是public的
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
int[] cloned = (int[]) luckyNumbers.clone();
cloned[5] = 12; // doesn't change luckyNumbers[5]
注意:如果原始对象中包含类似于String这样的不可变对象,浅克隆将不会有任何问题

<script type="text/javascript"></script><script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script><script src="http://pagead2.googlesyndication.com/pagead/js/r20110824/r20110719/show_ads_impl.js"></script><script src="http://pagead2.googlesyndication.com/pagead/render_ads.js"></script><script></script>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值