1.1基于面向对象的开发过程
基本面向对象分析 OOA (Object Oriented Analysis)
基本面向对象设计 OOD(Object Oriented Design)
基于面向对象编程 OOP (Object Oriented Programming)
1.2 面向过程和面向对象
面向过程开发 : 将系统按照事物的发展顺序设计,每个过程用函数体现(c语言 ,核心是函数)
面向对象开发 : 将系统当中存在事物归类,按照类别体现(java语言 ,核心是对象)
超市系统:
面向过程 : 1. 登录系统 2. 客户购买 3.收银 4. 进货 5. 退出 小红超市买苹果,付钱
面向对象 : 1.客户类 2. 商家类 3. 商品类 4. 结算订单
思考:五子棋游戏: 白方 黑方 棋盘系统类
1.3 面向对象的优势
1.最大的好处就是:代码重用 (避免代码的冗余)
2.不同的分析可以有不同的实现方式
1.4 面向对象的核心
面向对象的核心是类(对象)
类: 一类事物的抽象,概括统一说法 文具,抽象的说法
对象: 实际存在的个体 文具盒,具体的事物
学生类/张雨
老师类/小喵
动物类 /猫
类 : 类是对象的抽象说法,不占内存 ,分析写代码时候抽象的
对象 : 对象是类的具体化说法,占内存, 需要将类付诸于具体的对象的
注意:
先规划出类,设计类的特征和行为,将来把类的对象创建出来
1.5 类的构成
1.5.1属性
用于描述一类事物的特征,特点,如 学生的学号,性别,年龄....
1.5.2方法
用于描述一类事物的行为,动作和能力。 如学生吃饭,学习,运动....
类的语法规则:
控制权限 class 类名{ //属性 //方法 }
注意:
控制权限 public 不加 ; 只有一个加public 的,和文件名相同
class 声明类的关键字
类名 : 合法标识符 首字母大写 含义 不能是关键字
属性的语法规则:
控制权限 数据类型 属性名 [=初始值];
注意:
控制权限 public
数据类型 : 基本+引用类型
属性名: 同变量名
属性可以给初始值,也可以不给,有默认值: int 0 double 0.0 char \u0000 boolean false 引用 null
方法的语法规则:(重点)
控制权限 [修饰符] 返回类型 方法名(形参类型 形参变量,形参类型 形参变量){ 方法体; [return 返回值]; }
注意:
控制权限 public
返回类型 :
有返回类型 指定一种类型:int double String... ,指定返回值方法的最后一定要有return 值;
return 的值只能是一个,返回给调用者
无返回类型 void
方法名 同变量名
方法的参数:
方法声明时 形参 外部传入数据 ,必须逐一指明类型
方法调用时 实参
public class Student { //属性 public int sno ;//学号 public String sName;//姓名 public int age = 20; //年龄 public double weight; //体重 /** * 吃饭 */ public void eat() { System.out.println(sName + "在吃饭"); } /** * 跑步:返回跑公里数 */ public int run() { System.out.println(sName + "在跑步"); //return "公里:" + 10 + ";卡路里: " + 300; return 10; } /** * 算题 */ public int calc(int a,int b) { return a + b; } }
练习:定义一个动物类
属性: 名字,年龄,品种
方法:
1.睡觉: 返回睡几小时
2.吃饭 : 传入吃的东西,返回吃了多少克
3.跑动 :无参数 无返回
public class Animal { public String name; public int age; public String type; public double sleep() { return 10; } public double eat(String food) { System.out.println(name + "吃" + food); //返回吃掉 return 5.5; } public void run() { System.out.println(name + "在跑动"); } }
package com.qf.oop; public class Player { public char gender;//性别 public String name;//角色名 public int level; //等级 //键盘按键行走,根据性别决定速度 public String run(char code) { //确定速度 double speed = 0; if(gender == '男') { speed = 60; } else if(gender == '女') { speed = 40; } //确定方向 if(code == 'a') { return name + "向左以" + speed + "速度奔跑"; }else if(code == 's') { return name + "向下以" + speed + "速度奔跑"; }else if(code == 'd') { return name + "向右以" + speed + "速度奔跑"; }else if(code == 'w') { return name + "向上以" + speed + "速度奔跑"; }else { return name + "原地不动"; } } //攻击,传入武器 public void fight(String weapon) { System.out.println(name + "使用" + weapon + "战斗"); } }
1.5.4 对象的创建
类只是从设计的角度分析,如果使用类,必须创建该类的对象,真正的对象实例(堆中开辟内存)
类名 对象名 = new 类名();
Player monkey = new Player();
当类创建了对象,在堆中开辟内存,根据类中定义的属性个数决定开辟,堆中分配所有属性。
1.5.5 对象成员的访问
成员: 属性和方法
属性的调用:
对象名.属性名 //获取 对象名.属性名 = 值; //赋值
方法的调用:
数据类型 返回值 = 对象名.方法名(实参1,实参2...);//有返回值的方法 对象名.方法名(实参1,实参2...); //没有返回值
注意:
当调用方法时,如果类中方法设定过形参,调用的时候传入实参
形参: 类中方法声明定义的参数 , 形参是变量
实参: 对象调用方法时候的参数, 实参是数值
实参把值赋给形参, 形参 = 实参; 个数一致,类型一致,顺序一致
当方法内部设定了返回值,将值还给调用方法的位置,调用位置需要接收返回值
//类里定义位置 //weapon是形参 public void fight(String weapon) { System.out.println(name + "使用" + weapon + "战斗"); } //对象调用位置 monkey.fight("金箍棒");//实参
玩家测试类:
public class TestPlayer { public static void main(String[] args) { //创建玩家对象 Player monkey = new Player(); //System.out.println(monkey);//地址 //System.out.println(monkey.name);//null //System.out.println(monkey.level);//0 //System.out.println(monkey.gender);//\u0000 monkey.name = "美猴王"; monkey.level = 2; monkey.gender = '男'; System.out.println(monkey.name); System.out.println(monkey.level); System.out.println(monkey.gender); monkey.fight("金箍棒"); System.out.println("啊哈哈哈哈哈"); } }
1.5.6 变量的分类
变量分为两大类: 全局变量(属性),局部变量。
全局变量 | 局部变量 | |
---|---|---|
位置 | 类里方法外 | 方法里,方法的形参 |
权限 | 必须有权限 | 没有权限 |
默认值 | 有默认值 | 没有默认值 |
作用范围 | 整个类{ } | 所在的{ } |
生命周期 | 对象创建时产生,对象销毁消失 | 语句块{ } |
内存 | 堆(对象实例,大块空间) | 栈(临时变量存储) |
注意:
当全局变量和局部变量重名,局部变量会将全局变量覆盖
如果在方法中想要调用被覆盖的属性,this.属性
1.5.7 数组和对象结合
1.对象的属性是数组类型
public class Student { public String name; public String tel; public int no; //三门成绩 public double[] scores ; /** * 返回平均成绩 */ public double getAvg() { double sum = 0; for (int i = 0; i < scores.length; i++) { sum += scores[i]; } return sum / scores.length; } public void state(int day) { if(day >= 1 && day <= 5) { System.out.println("好好上课"); }else if(day == 6) { System.out.println("休息"); }else if(day == 7) { System.out.println("打游戏"); }else { System.out.println("非法日期"); } } public String info() { return "Student [name=" + name + ", tel=" + tel + ", no=" + no + ", scores=" + Arrays.toString(scores) + "]"; } }
测试类:
public class TestStudent { public static void main(String[] args) { Student s = new Student(); s.name = "老沈"; s.no = 110; s.tel = "8888666"; s.scores = new double[] {87,100,69}; double avg = s.getAvg(); System.out.println(s.info()); System.out.println("平均分数" + avg); s.state(2); } }
2.数组的元素是对象类型
对象数组: 数组的元素是一个个对象
语法:
类 [] 数组名 = new 类[大小];
public class Goods { public int no; public String name; public double price; public int counts; }
public class TestGoods { public static void main(String[] args) { //定义一个商品数组 Goods [] goods = new Goods[3]; Scanner sc = new Scanner(System.in); //动态赋值 for (int i = 0; i < goods.length; i++) { goods[i] = new Goods(); System.out.println("请输入商品名称"); goods[i].name = sc.next(); System.out.println("请输入商品价格"); goods[i].price = sc.nextDouble(); System.out.println("请输入商品编号"); goods[i].no = sc.nextInt(); System.out.println("请输入商品库存"); goods[i].counts = sc.nextInt(); } //数组遍历 for (Goods g : goods) { System.out.println(g.name + "," + g.price); } //获得最高价格的商品 double max = 0; int index = -1; for (int i = 0; i < goods.length; i++) { if(max < goods[i].price) { max = goods[i].price; index = i; } } System.out.println(goods[index].name); //控制台接收一个水果名,数组查找是否存在 equals() int index = -1; System.out.println("请输入您需要查找的水果名:"); String n = sc.next(); for (int i = 0; i < goods.length; i++) { if(goods[i].name.equals(n)) { index = i; break; } } if(index == -1) { System.out.println("没有该商品"); }else { System.out.println(goods[index].name + "," + goods[index].price); } } }
1.5.8 方法的调用
1.5.8.1 调用方式
对象调用,类创建出对象后,通过对象调用
public class Student { public String name; public void study() { System.out.println(name + "在认真学习"); } public static void main(String[] args) { Student s = new Student(); s.name = "张雨"; //对象调用方法 s.study(); } }
2.类中的方法互相调用
public class Student { public String name; //学习方法 public void study() { //听歌 music(); System.out.println(name + "在认真学习"); } //听歌 public void music() { System.out.println(name + "在听音乐"); } }
注意: 当a方法调用b方法的时候,先执行b,b执行完,再回来执行a。
public class Test { public void a() { b(); System.out.println("a"); } public void b() { c(); System.out.println("b"); } public void c() { System.out.println("c"); } public static void main(String[] args) { new Test().a(); // c b a } }
设定返回值:
public void a() { b(); System.out.println("a"); } public void b() { System.out.println("b"); System.out.println("2" + c()); } public int c() { System.out.println("c"); return 2; } // b c 22 a
递归调用(了解)
方法自己调用自己,递归调用必须要有一个结束条件,否则会造成死递归,堆栈溢出。
public class Num { /** * 求阶乘 * * 1! = 1 * 2! = 1! * 2 * 3! = 2! * 3 * .. * n! = (n - 1)! * n */ public int getJc(int n) { if(n == 1) { return 1; } return getJc(n - 1) * n; } public static void main(String[] args) { Num n = new Num(); int res = n.getJc(3); System.out.println(res); } }
综合练习:
public class ScoreCalc{ public int[] scores ; public double sum() { double sum = 0; for (int i = 0; i < scores.length; i++) { sum += scores[i]; } return sum; } public double avg() { return sum() / scores.length; } public void print() { System.out.println("平均成绩:" + avg()); } } /** 测试 */ public class TestScore { public static void main(String[] args) { ScoreCalc c = new ScoreCalc(); c.scores = new int[] {100,67,74}; c.print(); } }
1.5.8.2 方法参数个数
方法的重载: 展示方法的多样化(两个以上)
满足条件:
1. 方法名相同 2. 方法的参数一定不同: 个数,类型,顺序 3. 实参自动调用对应形参的方法
public class Student { /** * 计算两个数的和 */ public int sum(int a,int b) { return a + b; } /** * 计算三个数的和 */ public int sum(int a,int b,int c) { return a + b + c; } public double sum(double a,double b) { return a + b; } public double sum(double a,int b) { return a + b; } public double sum(int a,double b) { return a + b; } //相同的方法 // public int sum(int c,int d) { // // } //测试 sum(1,2,3); }
2.可变长参数(jdk5+)
用于设计方法的时候,不限制参数个数
控制权限 返回值 方法名([参数类型 参数变量 , ] 数据类型...可变长参数变量){ 方法体; return 值; }
注意:
可变长参数一定放在参数列表的最后
当定义了可变长参数,传递的实参个数可以不固定
在方法中,将可变长参数当作数组使用
public class Student { //可变长参数的方法,p参数变量 public int sum(int ...p) { int sum = 0; for (int i = 0; i < p.length; i++) { sum += p[i]; } return sum; } public static void main(String[] args) { Student s2 = new Student(); //任意个数的实参 int res = s2.sum(1,2,3,4,5,10); System.out.println(res); } }
1.5.8.3 方法调用参数传递
当调用方法的时候,传递实参,实参赋值给形参
1.基本数据类型 : 传递的是数值
2.引用数据类型 : 传递的是地址(引用)
public class Param1 { public static void change(int a,int[]b,Person p) { a = 10; //b = new int[] {2,4,6,8}; b[0] = 88; p.face = "哭唧唧"; } public static void main(String[] args) { int a = 20; int [] b = {1,2,3,4}; Person p = new Person(); p.face = "笑嘻嘻"; change(a,b,p); System.out.println(a);// 20 System.out.println(Arrays.toString(b)); //88 2 3 4 System.out.println(p.face); // 哭唧唧 } }
方法传参案例:
public class Param2 { public static void change(int x,int y,Birth b,String s) { //交换 int temp = x; x = y; y = temp; System.out.println(x + "," + y);// 200 100 b = new Birth(); b.date = 30; s = "abc"; } public static void main(String[] args) { int x = 100, y = 200; Birth b = new Birth(); b.year = 2002; b.month = 5; b.date = 9; //字符串类型传参比较特殊 String a = "123"; change(x,y,b,a); System.out.println(x + "," + y);// 100 200 System.out.println(b.date);//9 System.out.println(a);//123 } } public class Birth { public int year; public int month; public int date; }
1.6 构造方法
又叫做构造器,类中一种特殊的方法。(重点)
作用:
能够在创建对象的时候,给属性直接赋值(属性初始化)。
语法:
控制权限 类名(形参类型 变量, 形参类型 变量2...){ 方法体(给属性赋值) }
注意:
没有方法的返回声明
构造方法的方法名是类名
无参数的构造器:
public Birth() { System.out.println("创建了生日对象"); }
有参数的构造方法:
public Birth(int year,int month,int date) { this.year = year; this.month = month; this.date = date; }
构造方法的调用者 : JVM 自动调用
构造方法的调用时机: new对象
构造方法调用次数: 1次
构造方法的分类:
默认构造方法 当不定义构造方法的时候,系统会自动创建一个默认的构造方法
public 类(){ }
2. 当手动定义了构造方法,默认的无参构造方法就被覆盖。**因此,当创建构造方法的时候,一定加上无参构造。** 3. 构造方法可以实现重载: 根据方法的参数不同实现
构造方法和普通方法的区别:
构造方法 | 普通方法 | |
---|---|---|
返回类型 | 无返回声明 | 必须加声明 void |
调用方式 | JVM | 对象.方法() |
作用 | 初始化属性 | 自定义 |
调用时间点 | 创建对象的时new | 自定义 |
类中成员: 属性 + 方法+ 构造方法
思考: 类中普通方法是否可以是类名作为方法名? 可以,不推荐。 声明返回值类型
面向对象的三大基本特性: 封装性 , 继承性 ,多态性
1,封装性 信息隐藏,数据包装 ,安全性
2,继承性 代码继承 , 代码重用
3,多态性 一个父类多个子类 , 代码扩充性强
2.1 封装性
2.1.1 什么是封装?
将类中的成员(属性,方法,构造方法)信息隐藏,信息私有化,类之外的所有地方都不能访问。
2.1.2 作用?
1.保证数据的安全性
2.保证数据不被非法使用
2.1.3 如何实现封装?
1.需要封装的成员权限改为 private
控制权限: (限制成员的使用范围)
public 公开的 整个项目都可以使用
private 私有的 当前类
注意:企业开发中,为了保证数据安全,会将所有的属性都私有化。
封装的属性需要提供一组方法 setXX() 设置属性的值 getXX() 获得属性的值
public class Emp { //私有化属性 private int eno; private String ename; private double sal; //构造方法 public Emp() { } public Emp(int eno, String ename, double sal) { super(); this.eno = eno; this.ename = ename; this.sal = sal; } /** * 获得eno的值 */ public int getEno() { //可以添加判断 return eno; } /** * 获得薪资的方法 */ public double getSal() { return sal; } /** * 设置薪资的方法 */ public void setSal(double sal) { if(sal < 0) { System.out.println("薪资不合法"); }else { this.sal = sal; } } }
测试类:
public class TestEmp { public static void main(String[] args) { Emp p = new Emp(1,"jack",4000); //获取员工编号 System.out.println(p.getEno()); //设置薪资 p.setSal(-9); //获取薪资 //System.out.println(p.getSal()); } }
注意:
setXX() xx属性名 必须有参数
getXX() xx属性名 必须有返回值
2.1.4 封装后会怎样?
1.属性赋值的整个过程:
默认值
声明的值
构造方法(对象创建时第一次值)
setXX() (为了修改对象的属性值)
2.方法的封装(构造方法的封装)
封装方法: 当类中某些方法不希望外部类调用,仅限于类内部使用。
封装构造方法: 类不希望被创建对象(单例模式)
public void open() { //内部调取零件 startFixes(); System.out.println("电视打开"); } //封装方法 private void startFixes() { System.out.println("....零件1"); System.out.println("....零件2"); System.out.println("....零件3"); }
public class Math{ //封装构造方法 private Math(){ } }
单例模式:
public class Earth { private static Earth earth = new Earth(); private Earth() { } public static Earth getEarth() { return earth; } }
2.1.5 一个JAVA源文件的构成 Animal.java
包的声明(1次) |
---|
导包声明(n次) |
类的声明(n个): 属性,方法,构造方法 |
package 包; import 包; public class 类名{ //属性 //方法 //构造 } class 类名{ //属性 //方法 //构造 }
包:
在JAVA中相当于文件夹。
作用:
将逻辑相关的类存放在一起
限制类的访问
包声明:
package 根包.子包.子包; package com.qf.oop;
注意:
1.包名全部小写
2.包之间用.间隔, 命名 : 网站后缀.公司名.项目名 网站后缀.公司名.项目名.emp
3.包声明一定在源文件的首行有效代码
导入包声明:
import 根包.子包.*; //导入该包中所有资源 import java.util.Arrays; //导入JAVA中定义的类 import com.qf.homework.Student; //导入自定义的跨包类
注意:可以导入多个类
JAVA中提供包(API)
java.lang 核心包 System ,String ,Math....
java.util 工具包 集合,日期, Arrays ,Scanner
java.io 文件包 读写文件
java.net 网络
java.awt 图形窗口
注意:只有java.lang不需要手动导入 ;快捷键: ctrl + shift + o
2.2 继承性
2.2.1 类和类之间的关系
1.关联(包含) ”有“ has a
2.继承(泛化) ”是“ is a
3.依赖 ”用“ use a
1.关联(包含关系) A类中包含B类类型的属性 : 1对1 1对多(对象数组,集合) 多对多
class A{ //一对多 private B[] b; //一对一 private C c; } class B{ } class C{ }
例如: 订单类 : 一个账户类 ,n个商品类
2.继承 A类满足B类,A类继承B的代码
public class A extends B{ }
例如: 猫类继承动物类
3.依赖 A类的某个行为依赖于B类实现
public class Student{ public void goToSchool(Bike b){ } public void goToSchool(Car c){ } public void goToSchool(Bus b){ } // .. }
关联关系: A类包含B类,B类作为A类的属性
例子: 动物园 老虎 ; 餐馆 菜品 ; 运动会 体育项目 ; 美女类 衣服类
图书馆,书籍类 ; 美团 商铺 ; 超市 商品....
用户类:
package com.qf.oop.bank; public class User { private String name; private String card; private String tel; //包含 private Account acc; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCard() { return card; } public void setCard(String card) { this.card = card; } public String getTel() { return tel; } public void setTel(String tel) { this.tel = tel; } public Account getAcc() { return acc; } public void setAcc(Account acc) { this.acc = acc; } @Override public String toString() { return "user [name=" + name + ", card=" + card + ", tel=" + tel + ", acc=" + acc + "]"; } public User(String name, String card, String tel) { super(); this.name = name; this.card = card; this.tel = tel; } public User() { } }
账户类:
package com.qf.oop.bank; public class Account { private int accNo; private String pass; private double balance; public Account(int accNo, String pass, double balance) { super(); this.accNo = accNo; this.pass = pass; this.balance = balance; } public Account() { } public int getAccNo() { return accNo; } public void setAccNo(int accNo) { this.accNo = accNo; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } public double getBalance() { return balance; } @Override public String toString() { return "Account [accNo=" + accNo + ", pass=" + pass + ", balance=" + balance + "]"; } /** * 存钱 */ public boolean deposit(double mon) { if(mon > 0 && mon % 100 == 0) { balance += mon; return true; }else { return false; } } /** * 取钱 */ public boolean withdraw(double mon) { if(mon <= balance) { balance -= mon; return true; }else { return false; } } }
银行类:
package com.qf.homework; import java.util.Scanner; public class Banking { public static void main(String[] args) { int[] acc = { 1001, 1002 }; String[] pass = { "123456", "666666" }; double[] balance = { 5000, 1000 }; Scanner sc = new Scanner(System.in); while (true) { System.out.println("欢迎使用ATM系统:1.登录 2.退出系统"); System.out.println("请输入操作编号:"); int opp = sc.nextInt(); if(opp == 1) { System.out.println("请输入您的账户名:"); int no = sc.nextInt(); // 查找账户 int index = -1; for (int i = 0; i < acc.length; i++) { if (acc[i] == no) { index = i; break; } } if (index != -1) { System.out.println("请输入您的密码:"); String password = sc.next(); if (pass[index].equals(password)) { // 密码正确 System.out.println("登录成功"); while (true) { System.out.println("1.存钱"); System.out.println("2.取钱"); System.out.println("3.查看余额"); System.out.println("4.转账"); System.out.println("5.退出"); System.out.println("请输入操作编号"); int opr = sc.nextInt(); if (opr == 1) { System.out.println("请输入存款金额"); // 整百> 0 double money = sc.nextDouble(); if (money % 100 == 0 && money > 0) { balance[index] += money; } else { System.out.println("金额有误"); } } else if (opr == 2) { System.out.println("请输入取款金额"); // 判断 double money = sc.nextDouble(); if (money > 0 && money <= balance[index]) { balance[index] -= money; } else { System.out.println("余额不足"); } } else if (opr == 3) { System.out.println("当前余额:" + balance[index]); } else if (opr == 4) { System.out.println("请输入转账账户"); int no2 = sc.nextInt(); int index2 = -1; for (int i = 0; i < acc.length; i++) { if (acc[i] == no2) { index2 = i; break; } } if (index2 != -1) { System.out.println("请输入转账金额"); double m = sc.nextDouble(); if (m <= balance[index]) { // index2 账户+ balance[index2] += m; // index - balance[index] -= m; System.out.println("转账成功"); } else { System.out.println("金额不足"); } } else { System.out.println("无此账户,无法转账"); } } else if (opr == 5) { break; } else { System.out.println("操作错误"); } } } else { System.out.println("密码错误"); } } else { System.out.println("无此账户"); } } else if(opp == 2){ System.exit(0); }else { System.out.println("操作错误"); } } } }
测试类:
package com.qf.oop.bank; public class TestBank { public static void main(String[] args) { new Bank(); } }
2.2.2 什么是继承?
利用现有的类构建新的类的过程,代码的继承。
现有的类 : 父类 , 根类 , 超类 , 基类
构建新类 : 子类 , 派生类 , 衍生类
“是” , is a 如 : 老虎类 动物类 (子类 满足父类特性,子类相对具体,父类相对抽象); 鞋子类 服装类 ; 小学生类 学生类
2.2.3 继承的作用?
代码重用,避免代码冗余
2.2.4 如何实现继承?
控制权限 class 子类 extends 父类{ }
注意:
控制权限: public 或者 不加权限default
子类继承时的父类只能一个
父类:
public class Emp extends Object{ private int eNo; private String eName; private double eSal; public int geteNo() { return eNo; } public void seteNo(int eNo) { this.eNo = eNo; } public String geteName() { return eName; } public void seteName(String eName) { this.eName = eName; } public double geteSal() { return eSal; } public void seteSal(double eSal) { this.eSal = eSal; } public Emp(int eNo, String eName, double eSal) { super(); this.eNo = eNo; this.eName = eName; this.eSal = eSal; } public Emp() { } @Override public String toString() { return "Emp [eNo=" + eNo + ", eName=" + eName + ", eSal=" + eSal + "]"; } }
子类:
public class Manager extends Emp{ //奖金 private double bonus; public double getBonus() { return bonus; } public void setBonus(double bonus) { this.bonus = bonus; } @Override public String toString() { //super.toString() 父类的 return super.toString() + "Manager [bonus=" + bonus + "]"; } }
测试类:
Manager m = new Manager(); m.seteNo(2); m.seteName("rose"); m.seteSal(6000); m.setBonus(30000); System.out.println(m);
2.2.5 可以继承哪些?
可以继承: 非私有的属性和方法
不可以: 私有的,构造方法不可以继承的
因此: 子类中: 父类的成员 + 子类自己成员
2.2.6 继承后的问题
1.JAVA中的继承关系? 类和类之间只存在单继承(一个父类可以有多个子类,每个子类只能一个父类)
2.JAVA中继承关系时多层的,越是上层级越抽象,越是下层级越具体
Object是所有类的祖先类,顶级的,所有的类都直接或间接继承该类。
JAVA中的权限(重点)
public | protected | 缺省 | private | |
---|---|---|---|---|
含义 | 公开的 | 受保护的 | 友好的,默认的 | 私有的 |
范围 | 整个项目 | 同包访问,跨包继承 | 同包访问 | 当前类 |
范围排序: public > protected > 默认 > private
public class A { public int a = 10; protected int b = 20; int c = 30; private int d = 40; }
使用:
类,接口 : public ,缺省
属性: 四个
方法,构造 : 四个
局部变量 : 没有权限
2.2.7 方法的重载和重写(覆盖)
当多个方法具有相同方法名,如果希望共存,就需要重载或重写。
方法的重载:(方法的多样化) overload 原则: 1.方法名相同 2.参数必须不同(个数,顺序,类型) 3.权限和返回值不影响
方法的重写:(方法的覆盖,子类将父类继承的方法加以改造) override 原则: 1.方法名相同 2.参数必须相同 3.返回值必须相同 4.权限不能降低 public > protected > 默认 > private
package com.qf.oop; public class Father { /** * 重载 * @param t */ public void eat() { System.out.println("爸爸吃肉"); } public void eat(String t) { System.out.println(); } public int eat(String t,double k) { return 0; } } class Son extends Father{ /** * 重写 */ public void eat() { System.out.println("孩子喝奶"); } } public class TestFather { public static void main(String[] args) { Father f = new Father(); //爸爸吃肉 f.eat(); Son s = new Son(); //调用重写之后的方法 //孩子喝奶 s.eat(); s.eat("哇哈哈"); } }
总结重载和重写的区别:
重载 | 重写(覆盖) | |
---|---|---|
overload | override | |
目的 | 方法多样化 | 子类覆盖父类 |
参数 | 参数必须不同 | 参数必须相同 |
权限 | 不限制 | 子类不能降低权限 |
返回值 | 不限制 | 必须相同 |
位置 | 本类,父子类 | 父子类 |
次数 | n次 | 1次 |
异常 | 不限制 | 子类不能抛出更多异常 |
2.2.8 继承中的构造方法(难点)
子类不能继承父类的构造方法
子类需要手动调用父类的构造方法的:
public 子类(类型 形参变量 ){ //父类继承的属性 super(实参1,实参2); //自己定义的属性 this.xx = xx; }
子类手动调用父类的构造方法:
public Manager(int eNo, String eName, double eSal,double bonus) { //调用父类 super(eNo,eName,eSal); //自己 this.bonus = bonus; } //调用 public Emp(int eNo, String eName, double eSal) { super(); this.eNo = eNo; this.eName = eName; this.eSal = eSal; } //实例化: public class TestEmp { public static void main(String[] args) { //Emp e = new Emp(1, "jack", 4000); //System.out.println(e); Manager m = new Manager(2, "rose", 6000, 30000); // m.seteNo(2); // m.seteName("rose"); // m.seteSal(6000); // m.setBonus(30000); System.out.println(m); } }
3. 继承中,构造方法执行顺序: 祖先 > 父类 > 子类 4. 当构造方法的第一行没有super(参数) ,系统默认提供super(); 自动调用父类的默认构造,因此,保证空构造
public class GrandPa { public GrandPa() { super(); System.out.println("爷爷来啦"); } } public class Father extends GrandPa{ public String name; public Father(String name) { //super() System.out.println("爸爸来啦"); } // public Father() { // // } } public class Son extends Father{ public Son(String name) { //super() super(name); System.out.println("孩子也来啦"); } } public class TestSon { public static void main(String[] args) { Son son = new Son("jack"); } }
2.2.9 总结super和this的使用
super的用法:
在子类中调用父类的被覆盖的属性和方法
public class Student extends Person{ public String name = "小王"; @Override public String toString() { return "父亲:" + super.name + ";孩子:" + this.name; } }
public class Student extends Person{ public String toy; @Override public String toString() { return super.toString() + "toy=" + toy; } }
2.调用父类的构造方法
public Person(String name) { this.name = name; } public Person() { //super(); //默认的 }
public Student() { //super();//父类的空构造 } public Student(String name) { super(name); // 手动调用 }
注意: super() 调用构造方法,一定放在构造方法的首行。
this的用法:(当前对象,对象本身)
调用本类的属性和方法(尤其全局变量被局部变量覆盖)
public void setToy(String toy) { this.toy = toy; }
2. 调用本类的构造方法
public Emp(int empno, String ename, double sal, int deptno) { this(empno,deptno); //调用下面的构造方法 this.ename = ename; this.sal = sal; } public Emp(int empno, int deptno) { super(); this.empno = empno; this.deptno = deptno; }
注意:this() 调用构造方法,一定放在构造方法的首行。
2.3 多态性
2.3.1 什么是多态?
一个父类呈现出了多个子类形态,同一个动作展现出不一样的行为。
真正意义上的多态:
父类 多态对象 = new 子类(); // 向上转型
父类: Animal
子类: Tiger Rabbit Cat Dog Pig ....
Animal a = new Animal() Tiger t = new Tiger(); Cat c = new Cat(); Dog d = new Dog();
灵活的多态:
Animal a = new Dog();
生活中多态例子: USB : 风扇 , 键盘,鼠标,加湿器
USB b = new Mouse(); USB b = new KeyBoard();
2.3.2 多态的好处?
使得代码更加灵活,扩展性强
2.3.3 深入分析多态
Animal a = new Dog();
角度1:类型转换(编译角度)
当有继承关系:
父类 对象名 = new 子类(); // 自动转换 向上转型 子类 对象名 = (子类)new 父类(); // 强制转换 向下转型
角度2: 多态底层的运行
2.3.4 多态对象的访问
只能访问父类的成员,当且仅当子类覆盖父类方法,执行子类的方法。
2.3.5 多态设计的必要条件
1.继承,一个父类有多个子类 2.子类必须覆盖父类的方法 3.对象向上转型
父类:
public class USB { private String name; public USB() { } public USB(String name) { super(); this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public void use() { System.out.println("usb工作...."); } }
子类:
public class Mouse extends USB{ @Override public void use() { System.out.println(getName() + "usb鼠标工作...."); } public Mouse() { super(); // TODO Auto-generated constructor stub } public Mouse(String name) { super(name); // TODO Auto-generated constructor stub } } public class KeyBoard extends USB{ @Override public void use() { System.out.println(getName() + "usb键盘工作...."); } public KeyBoard() { super(); // TODO Auto-generated constructor stub } public KeyBoard(String name) { super(name); // TODO Auto-generated constructor stub } }
测试类:
public class TestUSB { public static void main(String[] args) { //USB u = new Mouse("罗技"); USB u = new KeyBoard("樱桃"); u.use(); } }
2.3.6 多态的实际应用
依赖关系的传参
public class Developer { /** * 依赖关系 * @param u */ public void useTool(USB u) { //调用覆盖的方法 u.use(); } }
开发人员测试工具:
public class TestUSB { public static void main(String[] args) { Developer d = new Developer(); d.useTool(new KeyBoard("樱桃"));//USB u = new KeyBoard("樱桃"); } }
注意: 构造方法不能重写,可以重载
2.3.7 instanceof 向下转型
用于判断某个对象具体是哪一种类型的:(看new)
对象 instanceof 类型(类/接口)
1.返回的是布尔类型: true(对象就是类型;对象是类型的子类类型) ; false(不是这个类型的)
2.如果对象和类型没有任何联系,出现编译错误
Cat c = new Cat(); Animal a = new Animal(); Animal a2 = new Tiger();//向上转型 多态对象 System.out.println(c instanceof Cat); // true 对象就是猫类型 System.out.println(c instanceof Animal); // true 对象就是猫类型 是Animal子类 System.out.println(a instanceof Animal); //System.out.println(c instanceof Student); //System.out.println(c instanceof Tiger);// Tiger Cat没有联系,没有兄弟关系 System.out.println(a instanceof Cat); // false 动物对象不一定是猫 System.out.println(a instanceof Object);//true System.out.println(c instanceof Object);//true System.out.println(a2 instanceof Cat);//判断是否是猫
目的是多态对象向下转型,获取子类中的资源:
Animal a = new Cat(); a.eat(); if(a instanceof Tiger) { Tiger t = (Tiger)a;//向下转型 t.ao(); }
3.1 abstract
3.1.1 什么是abstract?
抽象的,难以描述清楚,比较笼统。概念模糊。
3.1.2 abstract可以修饰哪些?
1.类 抽象类(抽象父类)
public abstract class 类名{ }
2.方法 抽象方法
控制权限 abstract 返回类型 方法名(形参类型 变量 ,形参类型 变量2 ) ;
3.1.3 抽象类的特征
1.抽象类一般作为父类,抽象类不能实例化
2.抽象父类强制要求必须有子类继承,子类将其描述清楚(必须有子类继承)
3.子类继承抽象父类之后,必须重写里面所有的抽象方法*,否则该类就成为抽象类*
4.抽象类中不一定都是抽象方法,可以有非抽象的; 有抽象方法的类必须是抽象类
5.抽象类是否包含构造方法?
抽象父类:
public abstract class Fly { private String name; /** * 抽象方法 */ public abstract void fly(); // public abstract void hua(); /** * 非抽象 * @return */ public String getName() { return name; } }
子类继承和覆盖:
public class Bird extends Fly{ @Override public void fly() { System.out.println("鸟飞行"); } } public class Plane extends Fly { @Override public void fly() { System.out.println("飞机飞行"); } }
测试类:
public class TestFly { public static void main(String[] args) { //抽象类不能实例化对象 //Fly f = new Fly(); //向上转型 Fly f = new Plane(); f.fly(); } }
注意:抽象父类有构造方法,给子类调用的。
3.1.4 抽象方法的特征
1.无法描述的行为,一定不能有方法体,不能加{ }
2.包含抽象方法的类一定是抽象类
public abstract void fly();
3.2 static
3.2.1 什么是static?
静态的,属于类的,不再属于某一个对象。
3.2.2 static可以修饰什么?
属性,方法,内部类
public class Test { //静态属性,类属性 private static int a ; /** * 静态方法,类方法 * @param args */ public static void test() { //局部变量不能static int b = 10; } }
3.2.3 静态属性?
加static的属性: 静态属性,类属性,是所有对象共享的资源,不再单独属于某个对象。例如: 共享汽车 私家车 ;网吧电脑,自己电脑;
不加的属性 : 实例属性, 是每个对象独有的资源。
人: 姓名 年龄 科目(灵长目类), 肤色
public class Person { //实例属性 private String name; private int age; //静态属性 private static String type = "灵长目"; }
检测静态属性的特点:
package com.qf.oop; public class TestStatic { public int i = 10; public static int j = 20; public TestStatic() { i ++; j ++; } public static void main(String[] args) { TestStatic t1 = new TestStatic(); System.out.println(t1.i + "," + t1.j);// 11 21 TestStatic t2 = new TestStatic(); System.out.println(t2.i + "," + t2.j);// 11 22 } }
堆栈图:
静态属性和实例属性的区别:(记住)
静态属性 | 实例属性 | |
---|---|---|
含义 | 属于类的,对象共享资源 | 每个对象独有 |
个数 | 一个类一份拷贝 | n份 |
内存分配 | 静态区 | 堆 |
初始化时机 | 类加载的时候(类使用之前) | 创建对象 |
调用 | 类.静态属性 / 对象.静态属性 | 对象.实例属性 |
调用:
public class TestStatic { public int i = 10; //实例属性 public static int j = 20; //静态属性 public TestStatic() { i ++; j ++; } public static void main(String[] args) { TestStatic t = new TestStatic(); // B 错误 其余都对 // A t.i // B TestStatic.i // C TestStatic.j // D t.j } }
3.2.4 静态方法?
静态方法只能调用静态的属性和方法,不可以调用非静态属性和方法
实例方法可以调用静态的方法
当静态方法就是要调用非静态: 只能创建对象
类名.静态方法()推荐的 / 对象.静态方法() 可以的
package com.qf.oop; /** * 测试静态方法的调用 * @author 86136 * */ public class Test2 { int a = 10; static int b = 10; public static void main(String[] args) { System.out.println(Math.random()); t1(); //非静态方法先创建对象才使用 Test2 ttt = new Test2(); ttt.t3(); } //静态方法 public static void t1() { System.out.println(b); Test2 ttt = new Test2(); System.out.println(ttt.a); t2(); //t3(); } public static void t2() { } //实例方法 public void t3() { System.out.println(a); System.out.println(b); t1(); t2(); } }
3.2.4 静态代码块?
静态代码块用于初始化静态的成员。
//静态代码块 static{ // 初始化静态属性 } //实例代码块 { }
1.位置 和构造方法同级
2.个数 可以有多个,按照顺序执行
3.时机 类加载时
4.次数 1次
5.顺序 静态 1> 实例 n > 构造 n
public class Test3 { public static int a ; public int b; static { //a = 20; System.out.println("静态代码块"); } // static { // a = 30; // } { System.out.println("实例代码块"); } public Test3(int b) { System.out.println("构造方法"); this.b = b; } }
3.3 final
3.3.1 什么是final?
最终的,最后的。
3.3.2 final修饰什么?
变量(属性+局部变量) 常量
方法
类
public final class TestFinal { public final String TEACHER = "Miss Yu"; public final void test() { final int YEAR = 365; } }
3.3.3 final修饰内容的特点?
final修饰的变量: 常量,一经定义不能改 ; final修饰的属性必须设置初始值,不可以使用默认值
final修饰的方法: 不能被重写
final修饰的类 : 不能有子类(最后的类)
JAVA中哪些类final修饰类: Math ,String,Integer
3.4 修饰符共用
abstract: 类 方法
static : 属性 方法
final: 变量 方法 类
1.不能混合使用
abstract和static
abstract和final
abstract 和 private
2.可以混用
static和final可以一起修饰属性和方法,不限制顺序
public class TestFinal2 { public final static int AGE = 20; public static final double PI = 3.14159265358979323846; public static void main(String[] args) { System.out.println(TestFinal2.AGE); System.out.println(TestFinal2.PI); } public static final void test() { } }
4.1 接口的意义
当一个子类继承了父类之后,不能具备其他父类的特性,接口可以使得类具备多种特性。
目的是解决单继承问题。
接口作为引用数据类型,抽象概念,只定义方法声明。 what is ?(定义是什么) How is ?(怎么样不管)
注意:开发中,设计和实现是分离的,接口是设计层面,架构师设计有什么; 类是实现层面,将接口中定义的内容具体实现
4.2 接口的定义
控制权限 interface 接口名{ }
注意:
1.控制权限 public 缺省
2.关键字 interface
3.接口名 : 同类名 , IWeapon , IVehicles
4.3 接口的实现
控制权限 class 实现类 extends 父类 implements 接口1,接口2...{ }
注意:
实现类可以同时实现多个接口,具备多种特性
继承只能一个父类 ,实现多个接口 (单继承,多实现)
案例:
//武器接口 public interface IWeapon { } //交通工具接口 public interface IVehicles { } //钢铁父类 public abstract class Iron { } //实现类 public class Tank extends Iron implements IWeapon,IVehicles{ } //抽象类实现 public abstract class UFO implements IWeapon, IVehicles{ }
注意: XXXImpl
4.4 接口中的成员
属性: 公开的静态常量 public static final = 初始值; 方法: 公开的抽象方法 public abstract 返回值 方法名(参数) ;
注意:
接口中属性和方法的修饰符全是默认的定死的,即使省略,必须是该类型
接口中没有构造方法,肯定不能实例化
public interface IWeapon { //静态常量 public static final int POWER = 100; int DEFENCE = 50; //抽象方法 public abstract void fire() ; String fix(int hour); }
4.5 实现类的实现
1.覆盖接口中所有的抽象方法
public class Gun implements IWeapon{ @Override public void fire() { System.out.println("开炮"); } @Override public String fix(int hour) { System.out.println("保养" + hour + "小时"); return "恢复,准备就绪"; } }
2.接口中的常量
接口名.属性,直接使用,不能修改
3.接口中多态
接口类型 多态对象 = new 实现类();
public interface Photograph { void shot(); } class Camera implements Photograph{ @Override public void shot() { System.out.println("相机拍照"); } } class Phone implements Photograph{ @Override public void shot() { System.out.println("手机拍照"); } }
public class Photographer { private static final String NAME = "义民"; /** * 使用设备拍照 */ public void takePhoto(Photograph p) { System.out.println(NAME + "拍照:"); p.shot(); } /** * 测试 */ public static void main(String[] args) { new Photographer().takePhoto(new Camera()); } }
4.6 类和接口的关系
关系 | 继承角度 | 关键字 | ||
---|---|---|---|---|
类和类 | 单继承 | 一个父类多个子类,每个子类一个父类 | extends | public class 子类 extends 父类{} |
类和接口 | 多实现 | 接口多个实现类,每个实现类实现多个接口 | implements | public class 实现类 implements 接口1,接口2...{} |
接口和接口 | 多继承 | 父接口可以有多个子接口,每个子接口继承多个父接口 | extends | public interface 子接口 extends 父接口1,父接口2...{} |
public interface A extends B,C{ } interface B { } interface C { }
4.7 抽象类和接口的区别(重点)
抽象类 | 接口 | |
---|---|---|
定义 | abstract class | interface |
属性 | 自定义 private | public static final |
方法 | 自定义 (抽象+非抽象) | public abstract |
构造方法 | 有 | 无 |
与类关系 | 单继承 | 多实现 |
相似点: 1. 都有抽象的方法存在 2.都不能实例化对象,都不能new
特例: jdk8+
public interface More { public abstract void test(); /** * jdk8+ 非抽象方法 */ public default void test2() { } }
5.1 枚举的意义
枚举表示一一列举,用于一组固定的离散值,比如:性别固定 男,女 ; 月份 1-12 ; 四季 , 星期, 十二生肖 , 四大名著,人民币面值(100,50,20,10,5,1), 交通灯(红色,绿色,黄色),26字母。
5.2 枚举的作用
代码的安全更高, 不会非法调用
降低代码的耦合度
代码的可读性增强,不会使用int,String代表其含义
5.3 枚举的声明
控制权限 enum 枚举名{ }
注意:
控制权限 : public 缺省
枚举名 : 首字母大写,合法标识符
5.4 枚举中的成员
控制权限 enum 枚举名{ 枚举常量 , 枚举常量2 ; }
5.5 枚举中的成员调用
枚举名.枚举常量
public enum Light { RED,GREEN,YELLOW; } public class TestLight { public static void main(String[] args) { System.out.println(Light.RED); } }
在switch中使用枚举:
/** * 交通 * @author 86136 * */ public class Traffic { //定义属性,切换完信号灯的颜色 private Light curr ; public Light getCurr() { return curr; } public void setCurr(Light curr) { this.curr = curr; } /** * 信号切换 */ public Light change(Light l) { switch (l) { case RED: curr = Light.GREEN; break; case YELLOW: curr = Light.RED; break; case GREEN: curr = Light.YELLOW; break; } return curr; } } /*测试*/ public class TestTraffic { public static void main(String[] args) { Traffic t = new Traffic(); //切换 System.out.println(t.change(Light.RED)); } }
5.6 工厂模型
23种设计模式: 单例模式,静态工厂模式,代理模式,生产者消费者....
单例模式: 获得对象实例只有一个
public class Singleton{ private static Singleton ins = new Singleton(); /** 使用方法返回一个实例*/ public static Singleton getSingleton(){ return ins; } }
案例:
public class Earth { private static Earth ins = new Earth(); private Earth() { } /**使用方法返回一个实例*/ public static Earth getEarth(){ return ins; } } public class TestEarth { public static void main(String[] args) { Earth e1 = Earth.getEarth(); Earth e2 = Earth.getEarth(); System.out.println(e1 == e2);//单实例 } }
静态工厂模式: 根据类型需要,生产对象。
public interface IShoe { public abstract void info(); } public enum ShoesType { ADIDAS,NIKE,ANTA; } public class Nike implements IShoe{ @Override public void info() { System.out.println("耐克鞋轻巧好穿"); } } public class Adidas implements IShoe{ @Override public void info() { System.out.println("阿迪好看,抗造"); } } public class Anta implements IShoe{ @Override public void info() { System.out.println("安踏永不止步"); } } /** * 静态工厂 * @author 86136 * */ public class ShoesFactory { private static IShoe shoe; /** * 多态属性 * @param type * @return */ public static IShoe make (ShoesType type) { switch (type) { case ADIDAS: shoe = new Adidas(); break; case NIKE: shoe = new Nike(); break; case ANTA: shoe = new Anta(); break; } return shoe; } } public class TestFactory { public static void main(String[] args) { //多态 IShoe shoe = ShoesFactory.make(ShoesType.NIKE); shoe.info(); } }
6.1 什么是内部类?
定义在类里的类,称之为内部类,外部的叫做外部类。
6.2 内部类的分类
① 静态内部类
② 实例内部类
③ 局部内部类
public class Outer { /** * 静态内部类 * */ public static class Inner{ public int a = 10; public static int b = 20; } /** * 实例内部类 * */ public class Inner2{ public int a = 30; } public void test() { //局部内部类 class Inner3{ } } }
静态内部类:
外部类.静态内部类
public class TestInner1 { public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); System.out.println(inner.a); System.out.println(Outer.Inner.b); } }
实例内部类:
new 外部类().new 实例内部类();
public class TestInner2 { public static void main(String[] args) { Outer.Inner2 inner2 = new Outer().new Inner2(); System.out.println(inner2.a); } }
局部内部类:看方法,外部类调用方法
public void test() { //局部内部类 class Inner3{ int a = 50; public void test2() { System.out.println("局部内部类"); } } Inner3 inner3 = new Inner3(); System.out.println(inner3.a); inner3.test2(); } public class TestInner3 { public static void main(String[] args) { Outer outer = new Outer(); outer.test(); } }
6.3 匿名内部类(重点)
① 该内部类没有名字
② 以上三种内部类都可以是匿名的,主要是局部内部类
抽象父类 变量名 = new 抽象父类(){ //抽象父类的子类的类体 } 接口 变量名 = new 接口(){ //实现类的类体 }
public interface IAnimal { void eat(); }
IAnimal animal = new IAnimal() { @Override public void eat() { System.out.println("小猫吃老鼠"); } }; animal.eat(); //lambda JDK8+ IAnimal animal2 = () -> System.out.println("小鹿吃草"); animal2.eat();