Java面向对象
第一章 类和对象
开发方法
- 结构化开发
- 面向功能划分软件结构
- 自顶而下
- 最小的子系统是方法
- 制约了软件的可维护性和可扩展性
- 面向对象开发
- 把软件系统看成各种对象的集合
- 系统结构较稳定
- 子系统相对独立
- 软件可重用性、可维护性和可扩展性强
1. 类
具有相同属性和方法的一组对象的集合
2. 对象
用来描述客观事物的一个实体,由一组属性和方法构成
万物皆对象
属性——对象具有的各种特征
每个对象的每个属性都拥有特定值
方法——对象执行的操作
3. 类和对象的关系 类是对象的抽象,对象是类的具体
类是抽象的概念,仅仅是模板;对象是一个你能够看得到、摸得着的具体实体
4.创建类
使用class关键字,类中有成员变量和成员方法
Java是面向对象的语言
所有Java程序都以类class为组织单元
关键字class定义自定义的数据类型
类将现实世界中的概念模拟到计算机程序中
public class 类名 {//类名首字母大写(大骆驼)
//定义属性部分
属性1的类型 属性1;
…
属性n的类型 属性n; //成员变量
//定义方法部分
方法1;
…
方法m; //成员方法
}
定义类的步骤:
(1)定义类名
(2)编写类的属性
(3)编写类的方法
类图
使用类图描述类
用于分析和设计“类”
直观、容易理解
5. 创建和使用对象
创建对象 使用new关键字
类名 对象名 = new 类名();
School center = new School();
调用对象成员:使用“.”进行操作
调用对象下的属性和方法:使用 对象名.属性名||对象名.方法名
center.name = "课工场"; //给name属性赋值
center.showCenter(); //调用showCenter()方法
6.类的方法
方法是个“黑匣子”,完成某个特定的应用程序功能,并返回结果
类的方法定义类的某种行为(或功能)
定义方法
语法:
访问修饰符 返回值类型 方法名([参数列表]){//(1)驼峰命名规则(小骆驼)(2)有意义
[return 返回值]
}
注意:
访问修饰符目前使用public公共的
没用返回值时使用void,return 可以省咯
方法名一般使用小骆驼命名
方法的返回值
两种情况
1.如果方法具有返回值,方法中必须使用关键字return返回该值,返回值类型为该值的类型;返回值只能有一个
public class Student{
String name = "张三";
public String getName(){
return name;//作用: 跳出方法、返回结果
}
}
2.如果方法没有返回值,返回值类型为void
方法调用
方法之间允许相互调用,不需要知道方法的具体实现,实现重用,提高效率
方法调用时,原方法有参数,调用时要传参,参数个数和参数类型要一致
观察之前编写过的方法,都是无参方法
7.成员变量(全局变量)和局部变量
成员变量(全局变量)与局部变量区别
成员变量有默认值,局部变量没有
作用域不同
以{ }为准
变量声明的位置决定变量作用域
变量作用域确定可在程序中按变量名访问该变量的区域
在同一个方法中,不允许有同名局部变量
在不同的方法中,可以有同名局部变量
在同一个类中,成员变量和局部变量同名时,局部变量具有更高的优先级
第二章 方法与方法重载
1. 方法
语法:
<访问修饰符> 返回类型 <方法名>(<形式参数列表>) {
//方法的主体
[return 返回值]
}
//调用方法
对象名.方法名(实参列表)
注意:
调用方法时不能指定实参类型!
形参和实参数据类型要一致!数量也要一致!
public class Test {
public void calc1(int num){
num=num+1;
}
public void calc2(Student student){
student.age=student.age+1;
}
}
//测试类
Test test=new Test();
int n=8;
test.calc1(n);
Student stu=new Student();
stu.age=18;
test.calc2(stu);
System.out.println(n+"---"+stu.age);
// 8---19
基本数据类型和引用数据类型数据在传参时区别
基本数据类型 值传递(浅拷贝)
操作传递的是变量的值,改变一个变量的值不会影响另一个变量的值。
引用数据类型 地址传递(深拷贝)
赋值是把原对象的引用(可理解为内存地址)传递给另一个引用
数组,对象参数传递
3. 构造方法
无参的构造方法,与类同名,不含返回类型。
作用:初始化对象属性
访问修饰符 构造方法名 ( ) {
//初始化代码
}
/*
1.方法名与类名相同
2.无返回值
3.系统会自带无参构造方法,当有其他构造方法,会自动覆盖无参构造
*/
自定义构造方法
当类有了带参构造方法之后,系统不再自动生成默认的无参构造方法,若使用需重新定义
public Student(){}
public Student(String name,int age){
//构造方法一般都是public的,才能被系统调用。
this.name=name;
this.age=age;
}
自定义构造方法
> 方法名相同
> 参数项不同
> 与返回值、访问修饰符无关
带参构造方法this关键字
是对一个对象的默认引用这里用以区分同名成员变量
构造方法只能用于创建对象时,不能在程序中像调用其他方法一样调用它,但是可以用this()
的形式在其他构造方法中调用它
注意this()
必须在第一行
this关键字的用法
- 调用属性 this.属性名
this.health = 100;
this.name = "大黄";
- 调用方法 this.方法名
this.print();
- 调用构造方法 this() 必须在第一行
this();
this("小黑",100,100,"雄");
//如果使用,必须是构造方法中的第一条语句
4. 方法重载
- 同一个类中
- 方法名相同
- 参数个数或类型不同
- 与返回值、访问修饰符无关
第三章 封装与继承
1. 封装 隐藏类的内部实现细节,对外提供一个可访问的接口
面向对象三大特征之一 ——封装
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
封装的两个大致原则
把尽可能多的东西藏起来,对外提供便捷的接口
把所有的属性藏起来
步骤
- 设置属性为private
- 创建get和set方法
- 可以在方法中添加判断等代码
封装的好处
1.便于使用者正确使用系统,防止错误修改属性
2.有助于系统之间的松耦合,提高系统独立性
3.提高软件的可重用性
4.降低了构建大型系统的风险
2. 包
文档分门别类,易于查找和管理
使用目录解决文件同名冲突问题
包的作用
1. 包命名规范
-
包名由小写字母组成,不能以圆点开头或结尾
package mypackage;
-
包名之前最好加上唯一的前缀,通常使用组织倒置的网络域名
package net.javagroup.mypackage;
-
包名后续部分依不同机构内部的规范不同而不同
package net.javagroup.research.powerproject;
规范:
网络项目.公司名.项目.模块名
com.hz.oop03.test01
.test02
net
cn
2. 导入包
为了使用不在同一包中的类,需要在Java程序中使用import关键字导入这个类
package 当前类位于那个包下(package 必须在代码的第一行)
import 引入类(包名.类名)
import java.util.*; //导入java.util包中所有类
import cn.jtest.classandobject.School; //导入指定包中指定类
1. 系统包:java.util
2. 自定义包:cn.jtest.classandobject
*: 指包中的所有类,可以使用*但是不推荐
School :指包中的School类
3. 注意
-
一个类同时引用了两个来自不同包的同名类
必须通过完整类名来区分
com.hz.oop03.test01
-
每个包都是独立的,顶层包不会包含子包的类
-
package
和import的顺序是固定的package必须位于第一行(忽略注释行)
只允许有一个package语句
其次是import
接着是类的声明
3. 访问权限控制
4. static修饰符
static可以修饰
-
成员变量
静态变量,可以直接通过类名访问
class User {
//静态变量
public static final int ID = 1111l;
}
User.ID
- 成员方法
静态方法,可以直接通过类名访问
public static void show(){
System.out.printIn("这是静态方法”);
}
User.show(); 不需要new
- 代码块
静态代码块:只加载一次(当Java虚拟机加载类时,就会执行该代码块)
static {
代码逻辑
一般用于程序初始化信息
}
JVM
加载类时,加载静态代码块如果有多个静态块,按顺序加载每个静态代码块只会被执行一次
public class StaticTest {
static int num=100;
static{
num+=100;
System.out.println(num);
}
static{
num+=100;
System.out.println(num);
}
}
StaticTest st1=new StaticTest();
StaticTest st2=new StaticTest();
System.out.println(StaticTest.num);
static变量
一、类的成员变量包括
-
类变量(静态变量)
-
被static修饰的变量
-
在内存中只有一个拷贝
-
类内部,可在任何方法内直接访问静态变量
-
其他类中,可以直接通过类名访问
-
-
实例变量
-
没有被static修饰的变量
-
每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响
-
二、static变量的作用:
- 能被类的所有实例共享,可作为实例之间进行交流的共享数据
- 如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量类型,从而节省内存空间
static方法
-
静态方法:可直接通过类名访问
- 静态方法中不能使用this和super
- 不能直接访问所属类的实例变量和实例方法
- 可直接访问类的静态变量和静态方法
-
实例方法:通过实例访问
- 可直接访问所属类的静态变量、静态方法、实例变量和实例方法
-
静态方法必须被实现
main()就是最常用的静态方法
常见错误
class Dog {
private String name = "旺财"; // 昵称
……
public void play(int n) {
static int localv=5; //在实例方法里不可以定义static变量
health = health - n;
System.out.println(name+" "+localv+" "+health+" "+love);
}
public static void main(String[] args) {
Dog d=new Dog();
d.play(5);
}
}
5. 继承
继承是Java中实现代码重用的重要手段之一。
Java中只支持单根继承,即一个类只能有一个直接父类
使用继承优化设计:将重复代码抽取到父类中
修改后:子类与父类是is-a关系(继承关系)
- 方便修改代码
- 减少代码量
使用继承
编写父类
[访问修饰符] class Pet {
//公共的属性和方法
}
编写子类,继承父类
[访问修饰符] class Dog extends Pet {
//子类特有的属性和方法
}
extends 继承关键字
单根继承
- 子类可以调用父类属性和方法
- 父类调用不到子类独有的属性和方法
- 子类构造方法默认调用父类无参构造方法
6. super关键字
子类访问父类成员
调用父类构造方法:
super();
super(name);
调用父类属性:
super.name;
调用父类方法:
super.print();
super关键字来访问父类的成员
- 使用super关键字,super代表父类对象
- super只能出现在子类的方法和构造方法中
- super调用构造方法时,只能是第一句
- super不能访问父类的private属性和方法
继承条件下的构造方法
继承条件下构造方法的调用规则
-
子类构造方法没有通过super显式调用父类的有参构造方法,也没通过this显式调用自身其他构造方法
系统默认调用父类的无参构造方法
-
子类构造方法通过super显式调用父类的有参构造方法
执行父类相应构造方法,而不执行父类无参构造方法
-
子类构造方法通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则
理解继承
-
子类继承父类的什么?
- 继承public和protected修饰的属性和方法,不管子类和父类是否在同一个包里
- 继承默认权限修饰符修饰的属性和方法,但子类和父类必须在同一个包里
-
子类可以继承父类的所有资源吗?
不可以,不能被继承的父类成员有:
- private成员
- 子类与父类不在同包,使用默认访问权限的成员
- 构造方法
访问修饰符protected可以修饰属性和方法,本类、同包、子类可以访问
使用继承
-
继承与真实世界类似
- 符合is-a关系的设计使用继承
-
继承是代码重用的一种方式
- 将子类共有的属性和行为放到父类中
总结
实现封装的步骤是什么?
- 修改属性可见性。设为private,防止错误的修改
- 创建公有的getter/setter方法。用于属性的读写
- 在getter/setter方法中加入属性控制语句。对属性值的合法性进行判断
常用的访问权限控制符有哪些?
public、protected、default(默认,不加修饰符)、private。
访问权限分别为:
- public:所有类可访问
- protected:同包类及不同包的子类可访问
- default:同包类可访问
- private:仅本类可访问
继承有何好处?
- 方便修改代码
- 减少代码量,提高代码重用性
如何实现继承?
创建父类,写公共的属性和方法
创建子类extends继承父类,写子类特有的属性和方法
第四章 方法重写与多态
1. 方法重写
方法的重写或方法的覆盖(overriding)
- 子类根据需求对从父类继承的方法进行重新编写
- 重写时,可以用super.方法的方式来保留父类的方法
- 构造方法不能被重写
方法重写规则
- 方法名相同
- 参数列表相同
- 返回值类型相同或者是其子类
- 访问权限不能严于父类
- 父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法
- 子类可以定义与父类同名的静态方法,以便在子类中隐藏父类的静态方法(注:静态方法中无法使用super)
- 父类的私有方法不能被子类覆盖
- 不能抛出比父类方法更多的异常
方法重写vs方法重载
2. Object类
Object类是所有类的父类
public class Pet extends Object {
……
}
Object类被子类经常重写的方法
Object类的equals()方法
- 比较两个对象是否是同一个对象,是则返回true
- 操作符
==
简单数据类型,直接比较值。如1==2
引用类型,比较两者是否为同一对象
(1)Object类的equals()方法与==没区别
(2)当有特殊需求,如认为属性相同即为同一对象时,需要重写equals()
(3)Java.lang.String
重写了equals()方法,把equals()方法的判断变为了判断其值
3. 多态
什么是多态
频繁修改代码,代码可扩展性、可维护性差
使用多态优化
同一种事物,由于条件不同,产生的结果也不同
多态:同一个引用类型,使用不同的实例而执行不同操作
多态的使用
1
2
又要给XXX
看病时,只需
- 编写
XXX
类继承Pet类(旧方案也需要) - 创建
XXX
类对象(旧方案也需要) - 其他代码不变(不用修改Master类)
方法重写是实现多态的基础
3
Pet类的toHospital()
如何实现呢?
toHospital()
不需要有具体的实现- 抽象方法
- Pet类声明为抽象类
- 实例化Pet毫无意义
public abstract class Pet {
}
向上转型
向上转型:父类的引用指向子类对象,自动进行类型转换
//测试方法
Pet pet = new Dog();
//<父类型> <引用变量名> = new <子类型>();
pet.setHealth(20);
Master master = new Master();
master.cure(pet);
- 此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法
- 此时通过父类引用变量无法调用子类特有的方法
向下转型
向下转型:将一个指向子类对象的父类引用赋给一个子类的引用,即:父类类型转换为子类类型。需强制类型转换
如果需要调用子类特有的方法则:
Dog dog=(Dog)pet;//将pet 转换为Dog类型
//<子类型> <引用变量名> = (<子类型> )<父类型的引用变量>;
dog. catchingFlyDisc();//执行Dog特有的方法
- 在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常
instanceof
Java中提供了instanceof
运算符来进行类型的判断
- 使用
instanceof
时,对象的类型必须和instanceof
后面的参数所指定的类在继承上有上下级关系
多态的应用
- 使用父类作为方法的形参,是Java中实现和使用多态的主要方式
- 使用父类作为方法的返回值,也是Java中实现和使用多态的主要方式
4. 抽象
abstract抽象类
**abstract抽象类:**不能被实例化,等待被继承
public abstract class Pet
abstract抽象方法
-
抽象方法使用abstract修饰
public abstract void show();
-
抽象方法没有方法体
-
抽象方法必须被重写
-
抽象方法必须在抽象类中,而抽象类中可以没用抽象方法
public abstract void print();
- 抽象方法没有方法体
- 抽象方法必须在抽象类里
- 抽象方法必须在子类中被实现,除非子类是抽象类
第六章 抽象类和接口
1. 抽象类与抽象方法
public abstract class Pet { //抽象类
public abstract void toHospital() ;//抽象方法
}
抽象类VS普通类
-
抽象类不能被实例化
但可以创建一个引用变量,其类型是一个抽象类,指向非抽象的子类实例
-
普通类可以被实例化
抽象方法VS普通方法
有无方法体
抽象类与抽象方法的使用
- 抽象类中可以没有抽象方法,但包含了抽象方法的类必须被定义为抽象类
- 如果子类没有实现父类的所有抽象方法,子类必须被定义为抽象类
- 没有抽象构造方法,也没有抽象静态方法
- 抽象类中可以有非抽象的构造方法,创建子类的实例时可能调用
2. 接口
接口是一种能力,接口是一种约束。
接口特性
-
接口不可以被实例化
-
实现类必须实现接口的所有方法
-
实现类可以实现多个接口
-
implements、多个接口使用逗号隔开
Java中的多继承
-
-
接口中的变量都是静态常量(public static final)
面向接口编程
关心实现类有何能力,而不关心实现细节
面向接口的约定而不考虑接口的具体实现
接口定义 interface关键字
public interface MyInterface {
public void foo();
//其他方法
}
所有方法默认都是:
public abstract
接口使用 implements关键字 多个接口使用逗号隔开
public class FdDoor extends Door implements Lock{
@override
public void open(){
System.out.println("开门.....");
}
}
-
接口中的成员变量
- 默认都是public static final的,必须显式初始化
-
接口中的方法
- 默认都是public abstract的
-
接口没有构造方法,不能被实例化
-
一个接口不能实现另一个接口,但可以继承多个其他接口
-
一个类必须实现接口抽象方法(implements),除非这个类也是抽象类
3. 抽象类vs接口
相同点
- 代表系统的抽象层
- 都不能被实例化
- 都能包含抽象方法
- 用于描述系统提供的服务,不必提供具体实现
不同点
- 在抽象类中可以为部分方法提供默认实现,而接口中只能包含抽象方法
- 抽象类便于复用,接口便于代码维护
- 一个类只能继承一个直接的父类,但可以实现多个接口
使用原则
- 接口做系统与外界交互的窗口
- 接口提供服务
- 接口本身一旦制定,就不允许随意修改
- 抽象类可完成部分功能实现,还有部分功能可作为系统的扩展点
面向对象设计原则
多用组合,少用继承
针对接口编程
针对扩展开放,针对改变关闭
总结
- Java中的接口
- 属性全都是全局静态常量
- 方法都是全局抽象方法
- 无构造方法
- 一个类可以实现多个接口,非抽象类实现接口时必须实现接口中的全部方法
- 抽象类利于代码复用,接口利于代码维护
第七章 异常
1. 什么是异常
异常是指在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序
- 异常分为Checked异常和运行时异常
- Checked异常必须捕获或者声明抛出
- 运行时异常不要求必须捕获或者声明抛出
2. 如何进行异常处理
Java的异常处理是通过5个关键字来实现的:try、catch、 finally、throw、throws
1. try-catch块
使用try-catch块捕获异常,分为三种情况:
1.正常
public void method(){
try {
// 代码段(此处不会产生异常)
} catch (异常类型 ex) {
// 对异常进行处理的代码段
}
// 代码段
}
2.出现异常
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
// 代码段 3
} catch (异常类型 ex) {
// 对异常进行处理的代码段4
}
// 代码段5
}
异常是一种特殊的对象,类型为
java.lang.Exception
或其子类
3.异常类型不匹配
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
// 代码段 3
} catch (异常类型 ex) {
// 对异常进行处理的代码段4
}
// 代码段5
}
- 在catch块中处理异常
-
加入用户自定义处理信息
System.err.println("出现错误:被除数和除数必须是整数 ");
-
调用方法输出异常信息
e.printStackTrace();
-
异常对象常用的方法
void printStackTrace()
输出异常的堆栈信息
String getMessage()
返回异常信息描述字符串,是printStackTrace()
输出信息的一部分
-
常见的异常类型
2. try-catch-finally
在try-catch块后加入finally块 finally块是否发生异常都执行
finally块中语句不执行的情况
不执行的唯一情况
public class ExceptionTest12 {
public static void main(String[] args) {
try {
System.out.println("try...");
// 退出JVM
System.exit(1); // 退出JVM之后,finally语句中的代码就不执行了!
} finally {
System.out.println("finally...");
}
}
}
try-catch-finally中存在return语句的执行顺序
public void method(){
try {
// 代码段 1
// 产生异常的代码段 2
} catch (异常类型 ex) {
// 对异常进行处理的代码段3
return;
}finally{
// 代码段 4
}
}
3. 多重catch块
- 引发多种类型的异常
- 排列catch 语句的顺序:先子类后父类
- 发生异常时按顺序逐个匹配
- 只执行第一个与异常类型匹配的catch语句
小结
try-catch-finally结构中try语句块是必须的,catch、finally语句块均可选,但两者至少出现之一
3. 声明异常
如果在一个方法体中抛出了异常,如何通知调用者?
throws声明某个方法可能抛出的各种异常,多个异常用逗号隔开
方式1:调用者处理异常
方式2:调用者继续声明异常
main()方法声明的异常由Java虚拟机处理
4. 抛出异常
除了系统自动抛出异常外,有些问题需要程序员自行抛出异常
使用throw抛出异常
throw和throws关键字的区别
throw | throws |
---|---|
生成并抛出异常 | 声明方法内抛出了异常 |
位于方法体内部,可作为单独语句使用 | 必须跟在方法参数列表后面,不能单独使用 |
抛出一个异常对象,且只能是一个 | 声明抛出异常类型,可以跟多个异常 |
5. 异常体系结构
6. 自定义异常
当 JDK
中的异常类型不能满足程序的需要时,可以自定义异常类
使用自定义异常的步骤
-
定义异常类
继承
Throwable
类、继承Excepion
或者RuntimeException
-
编写构造方法,继承父类的实现
-
实例化自定义的异常对象
-
使用throw抛出
7. 异常链
A方法调用B方法时,B方法却抛出了异常。那A方法继续抛出原有的异常还是抛出一个新异常呢?
抛出原有的异常
- A方法与B方法进行了关联,不便于代码的修改和扩展
抛出新的异常
- 异常链创建了新的异常但却保留了原有异常的信息
8. 异常处理原则
- 异常处理与性能
- 异常只能用于非正常情况
- 不要将过于庞大的代码块放在try中
- 在catch中指定具体的异常类型
- 需要对捕获的异常做处理