一、软件设计的概况
1. 软件的生命周期
-
产品提出需求
-
分析以及规划
-
软件编码及其调试(自测,联调)
-
上测试环境,给测试人员测试(提bug)
-
上 linux 生产服务器(运维人员维护,修改bug,系统的升级迭代)
-
被弃用
2. 软件设计原则
-
可重用性(减少重复的代码)
-
可拓展性
-
可维护性
-
高内聚、低耦合(模块之间要相互独立)
二、面向过程
-
面向过程:(Procedure Oriented Programming)pop是流程性的执行,需要去处理方法与方法之间的关系,或者是方法的调用
-
存在问题:从上往下设计的方式,分别考虑每个方法干啥,细分很难。
-
需求:做饭(买菜,做饭,做菜,吃饭,洗碗)
三、面向对象
1. 面向对象:(Object Oriented Programming )OOP ,站在对象的角度去考虑问题(处理类和类之间的调用和操作,把每一个类,拆分成不同的功能)
2. 面向对象三大特征:
-
封装:(可以把功能的特征的一些事物封装成一个对象,可以将细节隐藏起来,通过公共方法来调用,暴露功能)
-
继承:做到代码的复用
-
多态:把子类赋值给父类对象,调用的时候,还是去调用子类,表现出不同的特征
3.
类:
具有相同特征和行为的事物进行一个抽象。
-
电脑:cpu、内存、电源、主板
-
人:名字,身高,体重
4. 对象:是类的实例,类是对象的模板。
5. 面向对象和面向过程的区别:
-
面向过程会更平面化
-
面向对象会更加立体
-
你中有我,我中有你
四、面向对象特征之封装性
1. 为什么要引入封装性?
我们程序设计追求“高内聚,低耦合”
-
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
-
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露岀来。这就是封装性的设计思想。
2. 什么是封装
-
把对象的状态和行为看成了一个统一的整体,放到一个独立的模块中(类)
-
做到信息隐藏,把不需要外界看到的信息隐藏起来(private进行私有化),
向外提供方法
,保证外界的安全访问。
3. 封装的好处和意义:
-
提供了代码的复用性(可以减少重复代码)
-
使用者可以正确操作,方便使用系统功能
-
把实现细节隐藏起来,提供了安全性
4. Java规定的四种权限修饰符
通过对不同的方法属性设置不同的权限修饰符来达到对类进行封装的目的。
-
权限从小到大顺序为:private < 缺省 < protected < public
-
具体的修饰范围:
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
权限修饰符可用来修饰的结构说明
:
-
4种权限都可以用来修饰
类的内部结构
:属性、方法、构造器、内部类 -
修饰类
,只能使用:缺省、public
5. 封装的实现
5.1 规范
-
遵循 javabean 规范
-
set 后面的单词采用驼峰命名法,并且使用原单词 setAge(…)
-
get getAge();
private int age; public void setAge(int a){ age = a; } public int getAge(){ return age; }
-
变量:就近原则
-
this 关键字:代表当前对象的引用
-
-
需求:建一个学生类,name ,age,address 提供set get 方法。
public class Student { private String name; private int age; private String address; public Student(){ } public Student(String name){ this.name = name; } public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } public void setAddress(String address){ System.out.println(this); this.address = address; } public String getAddress(){ return address; } }
5.2 对于私有属性传参
-
使用 set get
-
使用构造器
-
5.3 注意:
-
static 静态的不能使用this 关键字,默认get 如果你不写 this,底层也会给我们加上 this
-
this 关键字也可以再构造器里面使用
五、面向对象特征之继承性
1. 引入继承性的好处
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用,提供了前提
2. 继承性的思想:
定义:基于某个父类对其进行拓展,定义新的子类,子类可以继承父类原来的属性和行为,并且可以增加父类没有的特性,或者覆盖父类中的某些特性
继承关系:is a 用是造句
继承的基础语法:
使用 extends 关键字
public class 子类类名 extends 父类类名{
写自己的特征
}
注意:
-
java 只支持单继承,允许多重继承(一个类只能有一个直接父类,但是可以有多个间接父类)
-
任何类都是 Object 的子类
创建对象时,构造方法如何执行
-
先执行父类构造器(先执行父类静态代码块,再执行子类静态代码块)
-
子类构造器
-
set get 方法
-
创建对象时,创建的是谁,打印 this 对象就是谁(多态)
3. 重写
3.1 重写:当父类特征不能满足子类特征的时候,可以对父类的方法进行重写
3.2 要求:
3.3 重写和重载的区别
-
子类的访问修饰符 >= 父类本身
-
父类不能使用 private 修饰
-
方法返回类型,子类 <= 父类
-
没有任何关系,只不过因为名字看起来相同,所以就拿来对比
-
重载:发生在同一个类中,方法名相同,参数列表不同,和方法的返回类型无关
-
重写:override
-
重载:overload
- 重载:解决了一个类中,相同功能方法名不同的问题
- 重写:解决子类继承父类,父类方法满足不了子类要求时,需要在子类里面重写
5. Object类
5.1 java.lang.Object类的理解
是Java中所有类的父类,类似于二叉树中的根节点,定义了一些通用的方法。
- 如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类
- 所的java类(除java.lang.Object类之外都直接或间接的继承于java.lang.Object类
- 意味着,所的java类具有java.lang.Object类声明的功能。
- java.lang.Object类中定义的一些方法
方法名 | 类型 | 描述 |
public Object() | 构造方法 | 构造器 |
public boolean equals( Object obj) | 普通方法 | 对象比较 |
public int hashCode() | 普通方法 | 获取Hash码 |
public String toString() | 普通方法 | 对象打印时调用 |
5.2. java.lang.Object类的说明:
-
Object类是所Java类的根父类
-
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
-
Object类中的功能(属性、方法)就具通用性。
-
属性:无
-
方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
wait() 、 notify()、notifyAll()
- Object类只声明了一个空参的构造器
- 数组也作为 Object类的子类出现,可以调用 Object类中声明的方法
5.3. equals()方法
- 是一个方法,而非运算符
- 只能适用于引用数据类型
public boolean equals(Object obj) {
return (this == obj);
}
使用说明
:
- Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
- 像==String、Date、File、包装类==等都重写了Object类中的equals()方法。==重写==以后,比较的不是两个引用的地址是否相同,而是==比较两个对象的”实体内容”是否相同==。
- 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的”实体内容”是否相同。那么,我们就需要对Object类中的equals()进行重写.
- 重写的原则:比较两个对象的实体内容是否相同.
手动重写举例:
class User{
String name;
int age;
//重写其equals()方法
public boolean equals(Object obj){
if(obj == this){//判断形参和比较对象的引用地址是否相同,相同直接返回true
return true;
}
if(obj instanceof User){
User u = (User)obj;
return this.age == u.age && this.name.equals(u.name);
}
return false;
}
}
自动生成的
,自动生成快捷键ctrl+alt+S,选择自动生成hashCode和equals方法
自动生成的代码:
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
回顾 == 运算符的使用:
== :运算符
-
可以使用在基本数据类型变量和引用数据类型变量中
-
如果比较的是基本数据类型变量:
比较两个变量保存的数据是否相等。(不一定类型要相同)
-
如果比较的是引用数据类型变量:
比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
使用说明
- == 符号使用时,必须保证符号左右两边的变量类型一致。
- 基本数据类型用==,引用数据类型用equals
5.4. toString()方法
toString()的使用:
-
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()方法
-
Object类中toString()的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
-
像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回”实体内容”信息。
-
自定义类也可以重写toString()方法,当调用此方法时,返回对象的”实体内容”
重写toString()
//自动实现
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
6. 子类对象实例化全过程
1. 从结果上看:继承性
子类继承父类以后,就获取了父类中声明的属性或方法。 创建子类的对象,在堆空间中,就会加载所父类中声明的属性。
2. 从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。
3. 强调说明:
虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
三、面向对象的特征之多态性
1. 多态性的理解:
可以理解为一个事物的多种形态。比如数据库的连接方式,我们定义好了数据库的连接,也规定了连接时的步骤,但是我们并不知道用户会采用什么数据库,在没有多态以前我们只能针对不同的数据库写不同的连接方法,而有了多态以后我们只需要定义好数据库的类并书写好连接方法,让所有的数据库继承数据库类并重写数据库连接方法。
这样我们在调用的时候只需要通过声明数据库类并指向数据库的子类的方式,**(即数据库类的引用指向继承了数据库类的具体实现类的对象)**就可以进行数据库连接。而不是需要针对不同的数据库书写不同的连接方式。
2. 何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
编译时和运行时类型不一致,产生了多态
代码举例:
Person p = new Man();
Object obj = new Date();
3. 多态性的使用:虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
- 总结:编译,看左边;运行,看右边。
-
对象的多态:在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
3.1多态性的使用前提
:
① 类的继承关系 ② 方法的重写
3.2多态性的应用举例:
举例一:
public void func(Animal animal){//Animal animal = new Dog();
animal.eat();
animal.shout();
}
举例二:
public void method(Object obj){
}
举例三:
class Driver{
public void doData(Connection conn){//conn = new MySQlConnection(); / conn = new OracleConnection();
//规范的步骤去操作数据
// conn.method1();
// conn.method2();
// conn.method3();
}
}
3.3 多态性使用的注意点
:
-
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
-
个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就==不能==再访问子类中添加的属性和方法
3.4 多态使用总结
-
多态的作用:提高了代码的通用性,常称作接口重用
-
使用前提:①需要存在继承或者实现关系 ②有方法的重写
-
成员方法:
- 编译时:要查看引用变量所声明的类中是否有所调用的方法
- 运行时:调用实际new的对象所属的类中的重写方法
-
成员变量:不具备多态性,只看引用变量所声明的类
4. 关于向上转型与向下转型:
向上转型:多态
4.1 为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。
4.2 如何实现向下转型:
使用强制类型转换符:()
Person p = new Man();
Man m1=(Man)p2;//向下转型
4.3 使用时的注意点:
① 使用强转时,可能出现ClassCastException的异常。
② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
③ 只有对象A是B的子类实例化对象或者在下层的子类,才能向下转型
4.4 instanceof的使用:
① a instanceof A:==判断对象a==是否是==类A的实例==。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
p instanceif Man//左边是变量名,右边是类的类型
5. 面试题:
1.谈谈你对多态性的理解?
- 实现代码的通用性。
-
举例:
- Object类中定义的public boolean equals(Object obj){ }
- JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
- 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
**2.多态是编译时行为还是运行时行为?**运行时行为
class Base {
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args) {
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
Base b = s;//多态性
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b == s);//true
System.out.println(b.count);//10
b.display();//20
}
}
- 若子类重写了父类方法,就意味着子类里面定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边