MODULE 5 OOP 面向对象程序设计
--------------------------------------------------------
Object Oriented Programming 缩写
Class类/Object对象
--------------------
万物皆对象
类:具有相同属性和行为的一组对象的组合
class 类名{
属性
构造器
方法
}
对象:类的一个实例
new 类名
OOP三大特性
1.封装Encapsulation
封装的目的:实现信息的隐藏
1)实现对象的属性信息的隐藏
2)对行为的实现细节的隐藏
外部用户无需关注方法的实现细节
2.Inheritance继承
类与类之间的关系
1)对象间具有一定的相似性
2)子类和父类之间一定要满足“is a”的语义关系
子类 is a 父类
Cat is a Animal
总结:
通常父类代表了一个大群体,子类是这个大群体中的小群体
3,多态Polymorhism
相同的行为,不同的对象具有不同的实现
java中的方法定义
--------------------------
1)一个方法允许存在多个修饰符,多个修饰符之间没有顺序的先后之别
2)返回类型位于修饰符之后,一个方法一定要有返回类型
对于没有返回值的方法,返回类型为void
3)方法中存在返回值时,要求方法中的每个分支都要有return,以确保在任何一种情况都有返回值
4)方法的参数可以是0~多个
访问同一类中的方法时可以直接调用
方法定义时:一定要指定参数类型
方法调用时:参数接收外部数据
5)构造器不是普通方法,构造器和普通方法的调用机制不同
参数传递*
-----------------------
区分形参和实参
形参:方法定义时的参数,还没有明确的取值
实参:方法调用时传递给形参的明确的取值
实参----> 形参
1.参数为基本数据类型
传值,将实参的值拷贝一份传递给形参
2.参数为引用类型
传引用 是将引用的对象的地址拷贝了一份传给了参数
this关键字
-------------------
attribute:name gender age 代表成员变量
成对的访问子:用于对属性进行赋值和取值的操作
public void setName(String name){name=name;}
//根据就近原则,找到的是局部变量name
this 代表当前类或对象
当成员变量名与局部变量名相同时,用this加以区分
public void setName(String name){this.name=name;}
封装
--------------------
1)属性信息的隐藏
在属性前面添加private修饰符,代表只能在该类内部访问
对外提供访问子,可以通过访问子约束属性的合理取值范围
方法重载 overloading
-----------------------
条件:
1)在同一个类的内部
2)定义了多个同名方法
3)参数类型或个数不同
4)返回类型不做要求 (可以相同也可以不相同)
优势:外部用户往往不关注调用的方法,由编译器自己判断
创建和初始化对象
---------------------------
创建对象:new Student();
1)为对象开辟空间;
2)为对象的成员变量进行默认初始化(0/false/null)
3)显式初始化
class Student{
String name="zhan";
int age=23;
}
缺点:每次创建一个对象,属性值都相同
4)构造器
当对象的属性值不确定时,通过构造器可以灵活控制
作用:
对属性进行初始化,不能涉及复杂的逻辑代码
允许定义多个构造器,提供多种初始化方案
知识点:
1)类中一定存在构造器
2)用户没有显式定义构造器java会默认分配无参构造器
public Student(){}
3)一旦用户显式定义了构造器,不会再分配无参构造器
如何在一个构造器中调用另一个构造器?
目的:简化代码
用this关键字
this(name,gender);
注意:
1)调用另一构造器用this(...) 可以简化代码
2)构造方法中的this()语句必须是第一条语句
总结this的两个用法:
1)成员变量和方法的局部变量名相同时
public void setName(String name){this.name=name;}
2)构造器重载时,调用另一构造器
this(param_list);
继承Inheritance
--------------------------
本质目的:实现代码的复用
语法:classs Subclass extends Superclass{...}
意义:
1)子类继承父类的属性和方法
2)子类可以添加属于自己特有的属性和行为
满足继承的条件:
1)"is a"的关系
2)java中的继承只能是单继承,子类只能有一个父类
class A extends B,C //错误
父类的哪些语法成分被子类继承?
--------------------------------------
1)父类的属性和非私有的方法可以被子类继承
父类的私有属性,子类只能通过访问子进行访问
2)父类的构造方法不能被子类继承,但是子类一定会调用父类构造器
父类构造器的调用
1)隐式调用
子类中没有super(...)显式调用父类构造器,则子类会默认调用父类的无参构造器
2)显式调用
若父类中没有默认的无参构造器时,子类构造器一定要显式调用父类的构造方法
语法:super(param_list);
参数类型一定要与父类构造器匹配
该语句一定要在第一句执行
super关键字
------------
super代表父类
1)子类调用父类的同名方法时
super.setAge(age);
2)子类构造器中调用父类的构造方法
super(param_list);
多态Polymorphism
--------------------------
对于不同的对象,相同的行为导致不同的结果,程序会在运行时动态决定调用的是哪个对象及其方法
静态类型
Person p; p的类型已经确定
动态类型
new Student(); 程序运行时
p代表的可以是Person对象本身,也可以是其子类对象
p=new Person(); p.display();//Person中的方法
p=new Student(); p.display();//Student中的方法
多态存在的三个条件:
1)一定要有子类继承父类;
2)一定要有子类重写父类的同名方法,即Overriding
3)父类的引用指向子类的对象
Casting强制类型转换
--------------------------
大类型赋给小类型时,需要进行强制类型转化
语法:(转换的目标类型)待转换的变量
如何避免任意的类型转化?
------------------------------
例如:
Animal a=new Bird();
Cat c=(Cat)a; //ClassCastExcption类型转换错误
不能将Bird类型对象转换为Cat类型
操作符instanceof
---------------------------
比较操作符,结果为true/false
用法: 引用型变量 instanceof 类名
作用:判断前面的引用型变量实际指向的对象的动态类型是否是后面的类自身或其子类,是返回true,否则返回false
通常对引用类型进行casting是,应该先用instanceof进行类型判定
方法重写/覆盖Overriding
-------------------------------------
1)一定是在子类和父类之间(与overloading不同)
2)方法名:参数列表和返回类型都相同;
3)可见范围不能被缩小
要求子类的同名方法的访问权限不能小于父类的方法访问权限
4)抛出的异常不能被子类放大
即子类方法抛出的异常必须和父类的同名方法抛出的异常相同,或者是父类方法抛出的异常类的子类
class Super{
public void method() throws IOException{}
}
class Sub extends Super{
public void method() throws Exception{}
}
MODULE 6 高级语言特征
---------------------------------------------------
重点:
1.static关键字 修饰变量 方法 初始化代码块
2.final关键字 修饰类 方法 变量
3.访问权限控制 public protected default(package private) private
4."=="和equals()比较
5.抽象类和接口*
6.内部类*
7.集合框架Collection**
8.反射机制reflection**
static修饰符
-----------------------------
1.修饰变量-----静态变量
----只能修饰成员变量,不能修饰局部变量
特点:该类的所有对象共享
static int maxAge=120;
1)static某种程度上类似全局变量
2)语法: 类名.静态变量名
p1.name 普通成员变量的访问
Person.maxAge 静态变量的访问
2.修饰方法---静态方法
特点:
1)该类的所有对象共享该方法
2)不能访问非静态的语法成分(普通方法/成员变量),只能访问静态变量和静态方法
3)在静态方法中访问类的普通方法和成员变量,一定要先创建该类的实例化对象
通常将只操作静态变量而不操作成员变量的方法定义为静态变量方法
4)父类中的静态方法不能被子类重写为非静态的
(即必须被子类覆盖成静态方法)
5)通常java中的工具类,提供的都是静态方法
3.static 修饰初始化代码块
----静态代码块
可以把代码写在方法外,一定要用static{...}括起来
class A{
static{
//code,没有方法名称可以调用
}
}
1)只能被执行一次,在类加载时(装入内存时)一次性的执行
2)可以存在多个静态代码块,JVM会合并执行
3)优先于类的构造器
通常对于只要求一次性初始化的静态变量,不写在构造器中,而用静态代码块
final关键字
-----------------------
特点:不可更改性
1.修饰变量----常量
一旦被赋值,就不能再修改
2.修饰方法
该方法只能在子类中被继承,但不能被重写Overriden
3.修饰类
不能有子类继承final类
final class A{}
class B extends A{} //非法
类是final,该类不会再有子类,里面的方法意味着就不能被重写,等同于方法final
static final -----------静态常量
Access Control访问权限控制
------------------------------------
public 共有,最大访问权限
protected 同一个包中或其他包中的子类可以访问
(package private)同一个包中的类可以访问
private 私有,只在本类内部可以访问
public > protected > package private > private
Object
----------------------
java中默认的祖先类,Object 相当于所有类的树根,单继承的父类最终会挂在Object类
class A(extends Object){}
1.toString()
返回的是字符串,用于描述对象的属性信息
class Person{
String toString(){
return name;
}
}
用法:Person p=new Person();
System.out.println(p);//默认调用p.toString()
System.out.println(p.display());//当p为null是,报错
"=="和equals()比较
------------------------
1.“==”
通常用于比较基本数据类型----比较值
如果比较的是引用类型--------比较的是引用地址
Person p1=new Person();
Person p2=new Person();
p1==p2 ? false 两个引用指向的不是同一个对象
p1=p2;
pq==p2 ? true p1和p2指向同一个对象
2.equals(Object obj)
p1.equals(p2); 比较的是两个对象的类型和内容是否相同
1)java 提供的类,例如,String
默认以实现了equals()方法,可以直接调用进行对象的比较
2)对于自定义的类,一定要重写equals()方法
public boolean equals(Object obj){
//1.比较类型是否相同,用instanceof
//2.再比较每个属性取值是否相同
//3.只有类型和内容都相同时,才return true;
}
代码如下:---------------------------------------------------------------------------------------------
public boolean equals(Object obj){
if(obj instanceof Person){
Person p;
p=(Person)obj; //强制类型转换
if((this.getName()==p.getName())&&(this.getSex()==p.getSex())&&(this.getAge()==p.getAge())){
return true;
}
else return false;
}
else return false;
}
或者:----------------------------------------------------------------------------------------------------------
public boolean equals(Object obj){
if(obj instanceof Person){
Person p;
p=(Person)obj; //强制类型转换
if((this.getName().equals(p.getName()))&&(this.getSex().equals(p.getSex()))&&(this.getAge()==p.getAge())){
return true;
}
else return false;
}
else return false;
}
包装器类Wrapper Class
--------------------------------
提供了四类八种基本数据类型相对应的包装器类
1)方便设计对象的操作
2)提供数值型和字符串类型间的转换方法
3)集合中存放的数据类型都为引用类型,不支持基本数据类型存储
用法:
1.jdk1.5之前 显式装箱/拆箱
装箱:基本数据类型 --> 引用类型
int i=20;
Integer inte=new Integer(i);
拆箱:引用类型 --> 基本数据类型
int j=inte.intValue(); ==> j=20
2.jdk1.5之后 自动装箱/拆箱
装箱:
Integer inte=30;
拆箱:
int i=new Integer(5); ==> i=5
int j=inte;
赋值号两边,一边是基本数据类型,一边可以是包装器类
抽象类和抽象方法
--------------------------------------
抽象类
abstract class A{}
现实世界对象---------(抽象)----------> 类
类---------> (抽象)------> 抽象类
Cat类 Dog类 ===> Animal抽象类
特点:
1)不能实例化对象(不能用new操作符)
Animal a=new Animal();//非法
Animal a; //合法
a=new Cat();
a=new Dog(); //合法
设计目的:
用抽象类的引用型变量去操作它的子类对象;
2)和子类满足"is a"语义关系;
3)单继承
抽象方法
abstract class A{
abstract void method();
}
特点:
没有方法的具体实现,只有方法的声明
设计目的:
通常定义了一种规范,表示所有的子类必须具有的行为,要求子类必须提供该行为的具体实现
知识点:
1)有抽象方法的类一定要声明为抽象类
2)抽象类当中可以有0~多个抽象方法,也可以有普通方法
3)子类中必须对抽象类中的抽象方法提供实现
4)抽象类可以继承抽象类,抽象类不能继承普通类
具体的类 继承 抽象
5)abstract 不能和 final 共同修饰一个类(不能实例化对象,不能被继承的类——无用)
6)抽象类中 有 构造器
给子类调用
思考: Fish Animal Food
接口Interface
-----------------------
间接地实现多继承
定义: public interface 接口名{}
特点:
1)不能实例化对象,是抽象类的另一种形式
2)接口中的语法成分:
a)接口中的变量都是静态常量(static final 写不写都是默认的)
b)接口中的方法都是抽象的(abstract 写不写都是默认的)
public interface A{
int ABC=100;
public void method();
}
定义了一种行为的规范,接口的实现类一定要对这些行为提供具体的实现,这些实现类之间可以毫无关联
Cat Dog Bird Animal Plane
public interface Flable{
publi void fly();
}
class Bird extends Animal implements Flable{
public void eat(){...}
public void fly(){...}
}
c)不存在 构造器
3)接口与接口以及接口与类之间的合法关系
a)类实现接口,可以实现多个接口
class A implements B,C{}
类可以既继承类有实现多个接口
b)接口可以继承多个接口
public interface C extends A,B{}
抽象类和接口的区别
---------------------------
相似处:
1)都不能实例化
2)抽象方法都只能声明不能实现,都代表一种行为规范
3)子类或实现类必须对抽象方法提供实现
设计角度:
都是为了让父类或接口的引用变量指向子类或实现类的对象
不同处:
1.语法角度
关键字 abstract class interface
--------------------------------------------------------------------------
变量 成员变量/静态变量/final 只能静态常量
方法 普通方法/抽象方法 只能抽象方法
构造器 存在,可以自定义 不存在
2.编程思想(设计理念)
语义关系 满足"is a" 没有
对类的抽象 对行为的抽象
(将子类的公有部分泛化出来) 实现类之间无关联
练习:
自定义类,以数组的形式实现对多个对象的增删改查以及遍历操作
该数据结构以线性方式管理数据(以索引号存取)
内部类Inner Class
--------------------------
class A{
int i;
void method(){}
class B{
int j;
void print(){}
}
}
将一个类定义在另一个类的内部
设计目的:
1)减少命名冲突,可以再不同的类中定义同名的类
内部类身上都携带有外部类信息
2)缩小类的使用范围,使其使用范围比包小,可以在一个类的内部或者方法的内部甚至表达式的内部使用
精确的确定类的使用范围
3)内部类可以访问外部类的私有语法成分
主要作用:
精确的控制类的使用范围
如何避免多次实例化一个内部类对象?
匿名内部类,控制内部类只能一次性的使用
通常将内部类的定义和使用放在一起
new 发动机(){ //借用父类或接口的名称创建内部类
void produce(){}
}; //不要忘了大括号后面的分号
内部类的分类:
----------------------------
1.静态内部类
参考静态方法或静态成员变量
class Outer{
private static int i;
static void method(){}
static class Inner{
public void method(){
i++; //可以访问静态语法成分
Out.method(); //访问外部类的方法
}
}
}
特点:
1)定义在类的内部,在该类内部都可以访问
2)不依赖于外部类对象,可以独立构建
3)只能访问外部类的静态语法成分
用法:
1)在外部类的内部如何创建?
Inner inn=new Inner(); //和普通类创建对象一样
2)在外部类的外部如何创建?
条件:内部类非私有
Outer.Inner oitest=new Outer.Inner(); //创建内部类的对象
Outer o=new Outer(); //这个语句只创建了外部类对象,内部类对象需要独立创建
2.成员内部类
参考普通成员变量和方法
1)定义在类的内部,方法外部
2)成员内部类紧密依赖于它所在的外在的外部类对象(不同于静态内部类)
3)可以访问外部类中的所有语法成分,包括私有的
用法:
1)在外部类内部创建内部类对象
Inner inn=new Inner();
2)在外部类外部创建内部类对象
普通成员变量和方法都紧密依赖于类的对象,一定要先创建对象,再访问其他的属性和方法,成员内部类也一样
Out.Inner oinn=(new Outer()).new Out.Inner();
3.局部内部类
进一步缩小了使用范围,只在方法内部使用
1)定义在方法内部,参考局部变量
2)可以访问外部类的所有语法成分
3)只能访问它所在的 【局部常量】
4)不能加public/protected/private/static 修饰符,无意义
类似局部变量,只在方法内部使用
4.匿名内部类
没有类名
class Outer{
public void method(){
class Inner implements IInterface{
void method(){}
}
Inner inn=new Inner();
}
}
1)也是定义在方法内部
2)没有类名,没有构造器,没有class/extends/implements关键字
借用其父类或接口的名称
3)将使用和定义放在一起,一次性地运用
改造:
class Outer{
public void method(){
IInterface inn=new IInterface(){
void method(){}
};
}
}
改造流程:
1)将class/extends/implements以及类名都删掉
2)创建对象时改用父类或接口的名称,写法区别于创建类的对象:
A a=new A(); //创建类A的对象
A a=new A(){...};
a)A是类:创建类A的一个匿名的子类对象
b)A是接口:创建的是接口A的一个匿名的实现类的对象
Collection 集合框架**
------------------------------------
java中的集合框架属于半成品,可以拿来做二次开发
---提供了常见的数据结构的实现,用来存储和管理多个对象
1)会看API
2)了解常见数据结构的特点
3)能够对各个集合类进行增删改查操作
特点:
集合中的类都用于存储多个对象(只能是Object类型)
包:java.util;
接口:
1.Collection接口 存储管理没有依赖关系的一组对象
|-------List接口 数据是顺序存储的,以索引号方式存取,允许对象重复,也允许null
|-------Set接口 存储的数据无序,不允许重复
|------SortedSet接口 具有排序功能的Set
2.Map接口 以键值对的形式管理一组成对的对象(key~value)
|------SortedMap接口 提供了具有排序功能的Map
问题:
1)Set中如何判断存储的对象是重复的?是否只要重写equals()就足够?
2)SortedSet提供了排序的功能,那么根据什么来排序?
比如:学生(学号,姓名,分数,年龄),每个属性都可以排序,
需要开发人员指定排序的规则
实现了:
Collection 无实现类
List实现类
ArrayList 采用数组的方式存储一组对象,根据索引号方式存取数据,增删效率低,查找高效
LinkedList 采用双向链表的方式,只能提供从前往后或从后往前一个个顺序查找元素,增删高效,查找低效
Vector 线程安全的ArrayList
Set 实现类
HashSet 数据无顺序,不重复
SortedSet实现类
TreeSet 提供了排序功能的Set
List练习:
创建一个银行账户类Account
属性:int code;
String name;
double balance;
方法:重写toString()方法,返回账户信息
创建ListTest.java 用ArrayList 对一组账户进行增删改查操作,并打印输出该List中的所有账户信息
Set练习:
创建SetTest.java 用HashSet对一组账户进行操作,放入一些重复的账户,验证重复数据是否成功放入
Set中的元素存放机制:
按照hashtable的散列算法存放,数值型是对表长取余数,那么对象呢?
如何获得对象身上的唯一的int型数据?
Object中的hashCode()方法:
返回该对象的hash代码
在Set中判断两个对象是否相同,需要重写equals()和hashCode()两个方法
Iterator迭代器
-------------------------------
对不同的数据结构提供了统一的遍历算法,解决的就是数据结构的遍历问题
public interface Iterator{
public boolean hasNext();
public Object next();
public void remove();
}
Iterator{
}
class ArrayList{ //这段小程序很重要
...
public Iterator iterator(){ //匿名内部类
return new Iterator(){
public boolean hasNext(){...}
public Object next(){...}
public void remove(){...}
};
}
...
}
以ArrayList为例实现遍历:
List list=new ArrayList();
Iterator iter=list.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
迭代器只适用于Collection分支,不适合Map分支
Comparator 比较器
-------------------------
指定对象之间的比较规则
public Interface Comparator{
public int compare(Obiject o1,Object O2);
public boolean equals(Object o);
}
public class CodeComparator implements Comparator{
public int compare(Object o1,Object o2){
//比较o1和o2的code属性
}
}
compare方法:
如果返回的结果>0,则o1>o2
如果返回的结果<0,则o1<o2
如果返回的结果=0,则o1=o2
Set set=new TreeSet();
练习:采用TreeSet数据结构管理一组Account,要求:
1)对Account提供按照code正排的方式
2)对Account提供按照balance倒排的方式
Map
----------------------
以键值对的方式存储和管理一组对象,一个key对应一个value
key ~ value
例:Student: code ~ Student
key值:唯一不重复 key值也是一个Object类型的对象
value:可以重复 也是Object类型
Map.Entry:存放key和value的关联关系,也是Object类型
Map的实现类
Hashtable 线程安全
HashMap 线程不安全的Hashtable
SortedMap的实现类
TreeMap 增加了排序功能
练习:创建MapTest测试类,存储一组Account(code,name,balance)
Set keySet() 获取Map中所有的Key对象的集合
Collection values() 获取Map中所有的Value对象的集合
Set entrySet() 获取Map中所有的key~value 的映射关系对象的集合
Reflection 反射 ***
--------------------------------
类 ----> 镜像
人 ----> 照镜子
class中的语法成分:
package信息
import信息
public class 类名 extends/implements 父类/接口{
成员变量:修饰符,数据类型,标识符
普通方法:修饰符,返回类型,标识符,参数列表,异常
构造器:修饰符,参数列表
}
包:java.lang.reflect;
需要import
类的镜像
java.lang.Class
成员变量的镜像
java.lang.reflect.Field
方法的镜像
java.lang.reflect.Method
构造器的镜像
java.lang.reflect.Constructor
A a=new A();//创建类A的对象a
String classname="A";
反射机制的核心:
在运行时动态获取已知名字的class类的相关信息,动态创建该类(调用构造器),并调用其方法或修改器属性(甚至是private的属性和方法)
introspection:内省/内观,看透class的能力
反射机制的运用:
1)通过镜像可以得到类的原始构造信息
2)在程序运行时,通过类名动态创建该类的对象(不是编译时)
在框架中广泛引用
3)破坏封装:通过反射可以获得类的成员变量/方法/构造器等私有特性
如何获得类的镜像
--------------------------
1)如果知道类名,根据类名.class 得到该类的镜像
Class c=String.class;//c代表String类的镜像
Class c1=int.class;//c1代表int基本数据类型的镜像
2)只知道引用型变量,调用变量的getClass()方法
String str="abc";
Class c=str.getClass();//c代表String类的镜像
3)只知道字符串形式给出的类名
Class.forName("类名");
String classname="java.lang.String";
Class.forName(classname);
没有Declared的方法
例:getFields() getMethods()..
返回的是class对象所表示的类或接口中的公有的属性或方法,包括继承
带有Declared的方法
例:getDeclaredFiedlds() getDeclaredMethods()...
返回的是所有的属性或方法(public/protected/default/private),但不包括继承的属性和方法
练习:
通过命令行传递一个数据结构(Collection)所表示的类名,通过反射机制动态创建该类的对象(new ArrayList()),往该数据结构中放入几个对象,然后再遍历输出该数据结构中的对象信息
例:"java.util.ArrayList" ---> args --> args[0]
分析:
1)拿到args[0]表示的类的镜像
Class c=Class.forName(args[0]);
2)创建该类的实例化对象
Object o=c.newInstance();
3)o ---> Collection ----> o.add(...);
newInstance()方法 调用的是该类的公有且无参的构造器
练习:
定义一个Data.java类,使其中的语法成分都为private
Data d=new Data();//无法在外部创建该类的对象(因为构造器私有)
通过反射机制:
1)创建该类的实例化对象
获取类的镜像 Data.class;
获取该类的构造器镜像 c.getDeclaredConstructors();
打开构造器为共有的 con.setAccessible(true);
2)调用其中的私有方法
3)修改器属性值