java note 3 面向对象
复习一下java,顺便把之前的那篇笔记重新排版。
1. 类与对象
类=属性+方法
面向对象三大特性:封装、继承、多态。
符合人的思维模式
面向对象三阶段:
- OOA(面向对象分析)
- OOD(面向对象设计)
- OOP(面向对象编程)。
UML图形语言&ROSE工具
1.1 类的定义
[修饰符(选) class 类名 extends 父类 implements 接口]
属性int id
称为 成员变量 或 实例变量,
变量属于对象级别,Student.id
无法访问。
static
修饰的变量称为静态变量。
类中包含:成员变量,成员方法,构造函数,静态方法。
1.2 对象创建
Student stu=new Student();
new
即开辟堆
stu
是引用类型(地址),指向 堆 中的对象。
程序员只能通过该地址间接操作堆中对象。成员变量没有手动赋值则可以自动初始化。
基本类型默认值:0,0.0 false \u0000 null.
再次强调:成员变量只属于对象,不属于类。
==
:基本类型比较值,引用类型比较地址。
public class customer{
public static void main(String[] args){
customer c = new customer();
String s = c.toString();
System.out.println(s);
}
}
单例模式
设计模式:重复利用的解决方案。设计模式分为三类,创建型、结构性、行为型。
单例模式在23中模式里最简单,尽量少用。
单例模式对于某一个类型只创建一个实例,以节省内存。
public class test{
public static void main(String[] args){
animal foo=animal.getInstance();
animal bar=animal.getInstance();
System.out.println(foo==bar);
}
}
单例模式分为饿汉式和懒汉式
//懒汉式
//非线程安全
public class animal{
private static animal a;
//将构造方法私有化--->无子类
private animal(){};
//提供公开方法
public static animal getInstance(){
if(a==null){
a=new animal();
}
return a;
}
}
//饿汉式
//特点:类加载阶段就创建了实例。
public class animal{
private static animal a=new animal();
//将构造方法私有化
private animal(){};
public static animal getInstance(){
return a;
}
}
单例模式没有子类,因为构造方法私有化,无法super()
。
1.3 编译
若b.java
引用a.java
,可直接编译b.java
1.4 构造方法/构造器/constructor
[修饰列表符] 构造方法名(形参){}
修饰列表:public/protected/private
构造方法作用:创建对象;初始化成员变量。
特点:
-
没有返回值类型
-
方法名和类名一致
-
可以重载
成员变量调用构造方法时才会赋值。
若没有手动添加构造方法,系统默认提供无参构造器。
如果手动提供构造方法,则系统不再提供任何构造方法。
所有构造方法都会执行Object()
。
1.5 封装
封装,即隐藏对象的属性和实现细节,仅对外公开接口。
private int age;
只能在本类中访问,可对外提供公开方法访问并进行安全控制。
public void setAge(int _age)
{
if(_age<0)
{return;}
this->age = _age
}
public int getAge(){}
成员方法,实例方法
static
修饰的方法称为静态方法。
1.6 继承
基本作用:代码重用;代码重写。
public class subClass extends superClass
支持单继承,间接继承。默认继承Object
,即SUN提供的java的根类src/java/lang/object.java
。
- 子类可以继承父类所有数据,包括私有数据。
- 子类不能直接访问父类私有数据,如
println(x.name)
,但能间接访问(getName()
方法)。 - 子类不能继承构造方法,但能
super()
调用。
方法覆盖override
class animal{
public void move(){System.out.println("动物移动");}
}
class cat extends animal{
public void move(){System.out.println("猫移动");}
public void eat(){System.out.println("猫吃鱼");}
}
方法覆盖发生在继承关系的两类之间。条件是具有相同返回类型,方法名,参数列表。
重写后的方法不能比被重写方法拥有更低的访问权限,不能抛出更宽泛的异常!!!!!!
- 私有方法(private)不能被覆盖
- 构造方法不能继承,所以也不能覆盖
- 静态方法(static)不能覆盖(无对象,则无继承一说)
- 覆盖针对成员方法,和成员变量无关
1.7 多态
多态可以增强项目扩展能力,面向抽象编程,降低代码之间耦合度,比如java.lang.Object.equals(Object obj)
public boolean equals(Object obj) {
return (this == obj);
}
多态分为向上转型(upcasting)和向下转型(downcasting)。
- upcasting:子转父,也称自动类型转换。
- downcasting:父转子,也称强制类型转换。
animal a1=new cat();//向上转型
a1.move();//猫移动
a1.eat();//error,需要强制类型转换,在这里即downcasting
cat c1=(cat)a1;//向下转型
c1.eat();//猫吃鱼
编译阶段知道a1
是animal
类型,所以编译阶段引用animal.move()
;(静态绑定)
运行阶段实际对象是cat
类型,所以运行阶段是cat.move()
。(动态绑定)
animal a2=new dog();
cat c2=(cat)a2;//java.lang.ClassCastException
为防止ClassCastException
,引入boolean
类型操作符instanceof
。
if(a2 instanceof cat){};
2. 一些特性及关键字
2.1 super
class employee{
String name="员工";
public void work(){
System.out.println("员工在工作");
}
}
class manager extends employee{
String name="经理";
public void work(){
System.out.println("经理在工作");
}
public void m1(){
super.work();
//System.out.pringln(name)
//System.out.pringln(this.name)
System.out.pringln(super.name);
}
}
manager m = new manager();
m.m1();//员工在工作 员工
super
代表当前子类的父类特征,不是引用类型,不指向父类。
super
可以用在成员/构造方法中:super();
通过子类的构造方法调用父类的构造方法(可用来初始化父类的私有变量),且手动调用super()
时必须放在构造方法第一行。
这样做不会创建父类对象,所以构造方法执行不一定创建对象。
this(参数)
:调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)一个构造方法如果第一行没有this(),也没有调用super(),则默认调用super()。
和this
一样,不能用在静态方法中。
2.2 final
final修饰的类无法继承,已经是最底层的东西。
final修饰的方法无法被覆盖。
final局部变量一旦赋值,无法改变。
final成员变量必须手动或构造方法初始化。
final成员变量一般static final
使用,以节省内存,因为值都相等,且变量名应大写。
举例:public static final double PI = 3.14;
抽象类和抽象方法不能被final修饰。
即final和abstract不共存,因为抽象类/方法是用来继承/覆盖的。
public final abstract class x{}
会报错
若Person.name
属性非final
,final Person p = new Person("Tom");
意思是p指向地址固定。p.name="Lucy";
不会报错。
3. 抽象类
定义:public abstract class A{}
抽象类无法new
实例化,但有构造方法,用来给子类创建对象。
抽象类可以定义抽象方法,不能有“{}”,且抽象方法只能出现在抽象类中,抽象类子类必须重写父类抽象方法。
public abstract class A{
A(){System.out.println("A...");}
//抽象方法
public abstract void m1();
public static void main(String[] args){
A a = new B();//输出A...B...
}
}
class B extends A{
//重写抽象方法
public void m1(){}
B(){
//super();没有创建父类对象。
System.out.println("B...");
}
}
4. 接口
接口也是一种引用类型,可看作类。
定义:[修饰符] interface 接口名{}
接口特性:
- 接口中只能出现常量和抽象方法。
- 接口是特殊的抽象类,特殊在完全抽象。
- 接口没有构造方法,无法实例化。
- 接口和接口之间可以多继承。
- 一个类可以实现(或者说继承)多个接口。
- 一个非抽象的类实现接口,需要将接口中所有的方法实现/重写/覆盖。
public interface A{
//常量必须用public static final修饰
//public static final一般省略
public static final double PI = 3.14;
byte MAX_VALUE = 127;
//抽象方法用public abstract修饰,一般省略。
void m1();
}
interface C extends A,B,C{}
class MyClass implements A{
//必须实现
public void m1(){}
}
接口作用:
- 分层。面向接口实现,面向接口调用。所有层面向接口,效率高。
- 使代码之间耦合度降低。
接口即协议、标准。
现实举例:灯泡场-灯泡尺寸-灯口厂,食客-菜单-厨师。
public interface CustomerService{
void logout();
}
public class CustomerServiceImp implements CustomerService{
public void logout(){
System.out.println("成功退出");
}
}
public class Test{
public static void main(String[] args){
//面向接口
//多态
CustomerService cs = new CustomerServiceImp();
cs.logout();
//复习编译和运行阶段
}
}
抽象类和接口都能满足时,优先选择接口。因为可扩展很重要,接口可以多继承,而且类可以继承其他类。
//Engine.java
public interface Engine{
void start();
}
//YAMAHA.java
public class YAMAHA implements Engine{
public void start(){
System.out.println("YAMAHA start.");
}
}
//HONDA.java
public class HONDA implements Engine{
public void start(){
System.out.println("HONDA start.");
}
}
//Car.java
public class Car{
Engine e;
Car(Engine e){
this.e=e;
}
public void testEngine(){
e.start();
}
}
//test.java
public class test{
public static void main(String[] args){
//create engine
Engine e1 = new YAMAHA();
//create car
Car c = new Car(e1);
c.testEngine();
}
}
5. Object类
查询API:src.zip
jre\lib\rt.jar里面是class文件
Object重点方法:toString,equals,finalize,hashCode
5.1 toString()
Object对象.toString()//java.lang.Object@xxxxxx
Person对象.toString()//Person@xxxxxx
该方法返回对象的字符串表示形式。
默认类名@java对象内存地址hash后的int型十六进制形式。
显然该形式实现结果不完整,需要重写。
public String toString(){
return getClass().getName()+"@"+Integer.toHexString(hashCode()); //this.hashCode()
}
class Person{
public String toString(){ //从api复制
return "Person[name-"+this.name+",age-"+this.age+"]";
}
}
Person p2 = new Person("xxx",1);
println(p2);//Person[name-xxx,age-1];
5.2 equals()
public boolean equals(Object o2){
return (this == o2);
}
该方法比较内存地址。
下面是重写的格式:
public boolean equals(Object o2){
if(this == o2) return true;
if(o2 instanceof Person){
Person p = (Person)o2;
if(P.name == name){return true;}
}
}
String
类型已经重写了equals()
:
String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2 //false
s1.equals(s2) //true
5.3 finalize()
Garbage Collection,也叫GC。
finalize()
由系统自动调用。
当对象的引用解除后,对象就变成了垃圾,GC在回收垃圾对象之前会自动调用finalize()
。
protected void finalize() throws Throwable{}
该方法可重写。
class Person{
public void finalize() throws Throwable{ //使用更高的访问权限,复习71
System.out.println(this+"is going to be collected!");
//重新引用
Person p = this;
}
}
Person p = new Person();
p = null.
//程序员只能建议回收垃圾
System.gc();
5.4 hashCode()
public native int hashCode();
native:本地方法调用(jre/bin/dll
文件)
返回int类型hash码值。
public class Test{
public static void main(String[] args){
Test t = new Test();
System.out.println(t.hashCode());
}
}
5.5 clone()
返回一个副本,有深克隆和浅克隆。
6. 包
包类似命名空间。
使用package
语句定义包(单包,复包),格式采用公司域名倒叙方式。
例如,package com.foobar.project.module;
即foobar公司project项目module模块。
完整的类名其实是带有包名的。
带有package语句要这样编译:
javac -d(irectory) 生成路径 源路径
,例如,javac -d . Mudule1
, 然后javac -d . Module2
可添加到目录下
运行:java com.foobar.project.module;
引用包
package com;
com.foobar.project.Test t = new com.powernode.oa.system.Test();
// or
package com.foobar.project;
Test t = new Test();
当然不会这样用滴。。。而是这样:
package ...;
import com.foobar.project.Module;
import com.foobar.project.*;
class ...
import
出现在package
之下,class
之上
//导入日期
import java.uril.Date;
println(new Date());//输出当前日期,不是地址。
java.lang
所有类是自动导入的。
7. 访问控制
修饰符 | 类的内部 | 同一个包里 | 子类 | 任何地方 |
---|---|---|---|---|
private | y | |||
缺省 | y | y | ||
protected | y | y | y | |
public | y | y | y | y |
private: 只能在本类中访问。
protected: 不同包不能访问,子类可以(基本给子类用)
default: 不同包不能访问
类的定义要么缺省,要么public。
8. 内部类
8.1 静态内部类
可等同看作静态变量
public class outerClass{
//static variable
private static String s1 = "a";
//member variable
private String s2 = "b";
//static method
private static void m1(){
System.out.println("static method execute.");
}
//member method
private void m2(){
System.out.println("member method execute.");
}
//static inner class
//can use any access permission modifier
static class innerClass{
//static method
public static void m3(){
System.out.println(s1);
//System.out.println(s2);//error
m1();
//m2();//error
}
//member method
public void m4(){
System.out.println(s1);
//System.out.println(s2);//error
m1();
//m2();//error
}
}
public static void main(String[] args){
outerClass.innerClass.m3();
innerClass inner = new outerClass.innerClass();
inner.m4();
}
}
生成文件:outerClass$innerClass.class
静态上下文中无法访问非静态变量(s2),也无法调用非静态方法。仅能访问外部静态数据。
8.2 成员内部类
- 可等同看作成员变量;
- 成员内部类中不能有静态声明;
- 能访问外部所有数据;
- 先创建外部类,再创建内部类
public class outerClass{
//static variable
private static String s1 = "a";
//member variable
private String s2 = "b";
//static method
private static void m1(){
System.out.println("static method execute.");
}
//member method
private void m2(){
System.out.println("member method execute.");
}
//member inner class
//can use any access permission modifier
class innerClass{
//static method can't be declared
//static void m3(){}
//member method
public void m4(){
System.out.println(s1);
//System.out.println(s2);//error
m1();
//m2();//error
}
}
public static void main(String[] args){
outerClass outer = new outerClass();
innerClass inner = outer.new innerClass();
inner.m4();
}
}
8.3 局部内部类(重点)
- 可等同看作局部变量
- 和局部变量一样,不能用访问权限修饰符修饰。
- 局部内部类里不能有静态声明。
- 从内部类成员方法里访问局部变量,需要声明最终类型final。但java8中不需要。
- 如果在局部内部类之后改变局部变量,会产生冲突。java8中产生冲突会报错。
public class outerClass{
public void m1(){
int i = 1;
//final int i = 1; //this is right
class innerClass{
//public static void m1(){};
public void m2(){
System.out.println(i);
}
}
i = 2;//error
innerClass inner = new innerClass();
inner.m2();
}
public static void main(String[] args){
outerClass outer = new outerClass();
outer.m1();
}
}
8.4 匿名内部类(重点)
- 类没有名字
- 优点:少定义一个类
- 缺点:无法重复利用
public class Test{
public static void t(customerService cs){
cs.logout();
}
public static void main(String[] args){
t(new customerServiceImp());
}
}
interface customerService{
void logout();
}
class customerServiceImp implements customerService{
public void logout(){
System.out.println("quit.");
}
}
public class Test1{
public static void t(customerService cs){
cs.logout();
}
public static void main(String[] args){
//anonymous class
t(new customerService(){
public void logout(){
System.out.println("quit.");
}
});
}
}
interface customerService{
void logout();
}
9. 类之间的关系
is a:继承关系(泛化)。Dog is an animal
is like a:类对接口的实现。类is like a 接口
has a:类的包含关系。关联关系。
9.1 泛化
泛化关系:类和类之间的继承关系,接口与接口之间的继承关系。
public class B extends A{}
public interface D extends C{}
uml图用实线三角形箭头。
9.2 实现
实现关系:类对接口的实现。
public interface E{
void m1();
}
public interface F implements E{
public void m1(){}
}
uml图用虚线三角形箭头。
9.3 关联
关联关系:类与类之间的连接,一个类可以知道另一个类的属性和方法。
通俗讲,就是在当前对象中有指向其它对象的引用。
public class Me{
String name;
Friend f;
Me(Friend f){
this.f = f;
}
}
//Friend.java
public class Friend{
String name;
String addr;
String tel;
Friend(String addr){
this.addr = addr;
}
}
//Test.java
public class Test{
public static void main(String[] args){
Friend f = new Friend("ShaanXi Xi'an");
Me m = new Me(f);
System.out.println(m.f.addr );
}
}
uml图用实线箭头:——>
9.4 聚合
聚合是关联关系的一种,是整体和部分的关系,如:汽车和轮胎。
整体与部分不相互依赖。整体无法决定部分的生命周期。
public class ClassRoom{
//ClassRoom和List集合属于关联关系,在同一层级上
//ClassRoom和Student属于聚合关系
List<Student> stud;
}
uml图用实线,整体端有个空心菱形。
9.5 合成
聚合关系基础上,整体与部分不可分。整体生命周期决定部分生命周期。
uml在聚合的基础上,空心菱形变成实心菱形。
9.6依赖
依赖关系:一个类在另一个类的方法里被使用。
public class Test{
public void m1(){
Person p = new Person(); //依赖
}
}
class Person(){}
uml图使用虚线箭头:- - - >