文章目录
面向对象
1.1 面向对象与面向过程
- 面向对象与面向过程的区别:
- 两者都是一种思想,思维方式
- 面向过程是具体化的,解决问题是一步一步的完成,实现就是对这些功能一步一步调用
- 面向对象是模型化的,他会把问题和数据装在一个盒子里面即类里面,用的时候调用,但具体怎么做的不归我们管,他是依托于面向过程的
- 面向对象的优缺点:
- 优点:
- 将复杂问题简单化,更贴近现实生活人们的思维角度
- 代码可维护性高 ,易扩展,比较灵活
- 面向对象适用于管理者,面向过程适用于执行者
- 缺点:
- 相较于面向过程性能较低,资源开销大
- 优点:
1.2 类和对象
-
类和对象的关系:
- 类是对象的模板,对象是类的实例化,开发中先有类再有对象
-
类的定义(创建)
public class 类名{ //属性 --> 成员变量 修饰符 数据类型 变量名; //如 public String name; // 功能-->成员方法(不被static修饰) public void|返回值类型 方法名(参数列表){ 实现功能代码 [ return 返回值;] //void时没有return } }
-
对象的创建
类名 命名 = new 类名(); // Phone phone = new Phone();
-
面向对象实例
/* 把大象装进冰箱 思路:1.该需求中涉及几种事务:大象 冰箱 2.模板类 大象 Elephant 冰箱 Fridge 3.类的成员 :属性 方法(具体的功能) */ public class Test{ public static void main(String[] args){ //创建大象对象 Elephant elephant = new Elephant(); //为属性初始化即赋值 elephant.name = "云云"; elephant.color = "白色"; elephant.type = "亚洲象"; //创建冰箱对象 Fridge fridge = new Fridge(); fridge.brand = "海尔"; fridge.color = "白色"; //调用Fridge 的方法把大象装进冰箱 fridge.open(); fridge.save(ele); fridge.close(); } } //定义大象类 class Elephant{ //定义成员变量-->即属性 public String name; public String color; public String type; } //定义冰箱类 class Fridge{ //定义成员变量-->即属性 public String brand; public String color; public double price; //定义冰箱的功能-->即方法 public void open(){ System.out.println("打开冰箱门"); } public void close(){ System.out.println("关闭冰箱门"); } public void save(){ System.out.println("一只名叫:"+elephant.name+"的大象被装进了冰箱"); } }
注意:方法的形参可以为任意类型:基本数据类型|引用数据类型|自定义的引用数据类型
-
典型列子:蛋糕师做蛋糕
public class Test { public static void main(String[] args) { //找到 蛋糕师 Maker maker = new Maker(); maker.name="胡歌"; maker.age = 40; maker.gender = false; Cake cake = maker.makeCake("正方形","冰激凌",300); Cake cake2 = maker.makeCake("心形","巧克力味",520); cake.show(); cake2.show(); } } //蛋糕 class Cake{ public String shape; public String taste; public double price; //展示 public void show(){ System.out.println(shape+"--->"+taste+"--->"+price); } } class Maker{ public String name; public int age; public boolean gender; //true女 false男 //制作蛋糕 public Cake makeCake(String shape,String taste,double price){ //制作蛋糕 Cake cake = new Cake(); cake.shape = shape; cake.price = price; cake.taste = taste; return cake; } }
注意:方法的返回值可以是任何类型:基本数据类型|引用数据类型|自定义的引用数据类型
1. 基本数据类型的变量=右边为数据值
2. 引用数据类型的变量=右边为对象
3. 成员变量只声明不赋值存在默认值 整数0 小数0.0 boolean:false char:空字符 引用数据 类型: null
4. 局部变量必须初始化,不初始化会报错,成员变量不初始化会有默认值不会报错
5. Java.lang 包是Java语言的核心类库,它包含了运行Java程序必不可少的系统类,使用该包 下的类和接口不需要使用import导入
1.3 构造器
又叫构造函数|构造方法
-
作用:为对象初始化信息
-
使用:跟随new关键字一起使用,不能单独调用
-
定义:
修饰符 类名(参数列表){ 初始化变量 } /* 注意:1.构造器的名字必须和对应的类名一致,且不能定义void|返回值 2.构造器也是特殊的方法,具有方法重载的特性->构造器的重载 3.如果一个类中没有显示的定义构造器,编译器会自动默认提供一个空构造(没有参数的构造器) 4.如果存在显示定义的构造器,编译器不会再为类型提供任何构造器了,包括空构造 5.构造器中可以存在return,根据需求定义,实现提前结束构造器,但是不能带出返回值 6.构造器帮助实现,在通过new关键字创建对象的同时为对象初始化信息 7.以后定义模板类,至少提供一个空构造,按需提供带参构造 */ public class Test { public static void main(String[] args) { //先创建对象,后赋值 //1.创建对象 //Dog dog = new Dog(); //2.赋值 //dog.name = "花卷"; //dog.age = 4; //dog.type = "二哈"; //创建对象的同时为成员变量赋值 Dog dog = new Dog("大白",-2,"萨摩"); dog.lookDoor(); //为对象的成员重新赋值 dog.name = "小白"; dog.lookDoor(); } } class Dog{ public String name; public int age; public String type; //空构造 /*public Dog(){ System.out.println("这是空构造......"); } public Dog(String dogName){ name = dogName; }*/ public Dog(String dogName,int dogAge,String dogType){ System.out.println("这是带参造......"); if(dogAge<0){ System.out.println("年龄不合法无法初始化..."); return; } name = dogName; age = dogAge; type = dogType; } public void lookDoor(){ System.out.println(name+"正在看家..."); } }
1.4 对象的内存分析
1.5 this关键字
指代当前new的对象
-
this应用
- 在构造器的首行用来调用本类中的其他构造器 —> this(参数列表)
- 区分局部与成员同名问题
- 默认就近原则
- 存在同名问题,指代成员需要通过this. (this后面有个. )调用
- 不存在同名问题时,指代成员直接使用省略this
-
this注意事项
- 不能多个构造器之间通过this相互调用
- this存储器指代对象的地址
- 在构造器中遇到的this指代对象,默认代表当前new的对象
- 在成员方法中的this默认指代调用成员方法的对象
- this不能使用在static修饰的方法中----> 因为static修饰的都属于类,不属于对象
-
测试用例
public class ThisTest { public static void main(String[] args) { //*匿名对象 : 只能在当前行使用一次 //何为匿名对象,只有new 和实例化对象,没有赋值给引用的 // 如 new Person() --->匿名对象 new Person("zhangsan",18,false).info(); Person p = new Person("lisi"); System.out.println("p----->"+p); p.info(); } } class Person{ public String name; public int age; public boolean gender; public Person(){ System.out.println("空构造..."); } //在构造器中遇到this指代对象,默认代表当前new的对象 public Person(String name){ System.out.println("一个参数构造..."); System.out.println("this------->"+this); this.name = name; } public Person(String name,int age){ System.out.println("两个参数构造..."); this.name = name; this.age = age; } public Person(String name,int age,boolean gender){ this(name,age);//想要调用其他构造器,this必须在首行 this.gender = gender; System.out.println("三个参数构造..."); } public void info(){ String name = "xixihaha"; //在成员方法中的this默认指代调用成员方法的对象 System.out.println(this.name+"-->"+this.age+"--->"+gender);//this.name = "zhangsan" this.age = 18 gender = false } }
1.6 static关键字
static:静态的 成员修饰符 :只能修饰成员不能修饰局部 静态的是属于类的
成员变量:声明在类中方法外的
局部变量:声明在方法中的
-
static可以修饰
- 修饰变量:被static修饰的变量叫 静态变量|类变量
- 修饰方法:-------> 静态方法|类方法
- 修饰块:-------->静态块
- 修饰类:-------->静态内部类
-
静态变量与静态方法的使用
- 类名.静态变量名 类名.静态方法名(参数列表)
- 对象.静态变量名 对象.静态方法名(参数列表)
-
注意事项
- 静态的是属于类的
- 成员的是属于对象的,只能跟随对象使用
- 成员变量存在于对象的内存空间中,有多少对象的堆内存空间,就存储多少分成员变量
- 静态变量是独一份的,当前类的多个对象共享的,存在于静态区中
- 在静态内容中使用: 只能直接使用静态内容,不能直接使用成员内容,需要跟随对象使用成员
- 在成员内容中使用: 可以直接使用成员,可以直接使用静态
成员:
静态:静态变量
非静态:实例:{ 实例变量 实例方法 }
-
测试用例
public class StaticTest { //成员 static int a = 5; //静态变量 int b = 10; //成员变量 public static void main(String[] args) { //局部 int i = 1; //类名调用静态内容 System.out.println(StaticTest.a); StaticTest.testStatic(); //对象 StaticTest cs = new StaticTest(); //只能通过对象调用成员 System.out.println(cs.b); cs.test(); //对象调用静态内容 System.out.println(cs.a); cs.testStatic(); } //静态方法 public static void testStatic(){ System.out.println("静态方法"); } //成员方法 public void test(){ System.out.println("成员方法"); System.out.println(a); System.out.println(b); } }
1.7 block块
block :块,又叫作用域 ---- > {}: 大括号就形成一个块
-
分类
- {}定义在方法中|语句块中 ----->局部代码块 ----->执行时机:跟随方法的调用
- {}定义在类中方法外 ------>构造块 ------>执行时机:跟随new一起使用
- static修饰的{}定义在类中方法外 ----->静态块 ------> 执行时机:类第一次加载
-
注意事项:
- 构造块中的代码会先于构造器中的代码之前执行,在编译期间构造块中的代码会被编译到要执行的构造器代码的最上面
- 如果存在多个构造块,从上到下一次执行
- 静态块会在类第一次加载完成之后进行执行,并且只执行一次
- 如果存在多个静态块,从上到下一次执行
- 构造块作用 : 为成员提供默认初始化
- 静态块作用 : 1.为静态内容提供默认的初始 2. 基础设置等的加载(数据库的驱动…)
-
测试用例
public class BlockTest { static int i = 10; String name; //静态块 static{ System.out.println("我是静态块1..."); } static{ System.out.println("我是静态块2..."); } static{ System.out.println("我是静态块3..."); } //构造器 public BlockTest(){ System.out.println("我是构造器..."); } public BlockTest(String name){ System.out.println("我是构造器..."); this.name = name; } //构造块 { System.out.println("我是构造块1..."); name = "zhangsan"; } { System.out.println("我是构造块2..."); } { System.out.println("我是构造块3..."); } public static void main(String[] args) { System.out.println("main----------"); //局部 int i = 5; //局部代码块 { System.out.println("我是局部代码块中的代码...."); i = 1; } System.out.println(i); System.out.println(new BlockTest("lisi").name);; } }
1.8 Debug
Debug 是程序员开发中必须要会使用的,有利于调试错误,我在这里介绍常用的几个
-
作用:
- 追踪程序的执行流程
- 定位异常出现的位置
- 观察程序执行过程中变量的变化情况
- 根据追踪程序的执行流程学习第三方框架的源码
-
步骤:
-
设置断点 在行号的后面鼠标单击设置断点
-
右键 —> Debug运行进行调试
-
1.Step over F8 : 下一步跳过|略过如果下一步是方法的调用,不会跟随方法的调用执行,直接进入下一步
-
2.Step into F7 : 步入 | 下一步进入如果下一步是自定义方法的调用,跟随方法的调用执行每一步
-
3.Step out shift+f8 : 下一步调出如果在方法的内部,下一步直接调出到方法调用处
-
4.Force step into alt+shift+f7 : 下一步强制进入如果下一步是方法的调用,包括jdk源码方法的调用,强制进入,跟随执行
-
5.Run to Cursor alt+F9 : 直接运行到鼠标光标所在位置
-
1.9 package 与 import
包机制:文件夹 帮助管理众多的资源 提供了多重命名空间
-
导包
- 指明要使用类型的位置
- 如果要使用其它类型,可能涉及到导包问题
- 不需要导包的类
- java.lang包下的内容
- 同包类
-
导包的方式
- 使用的位置指定类型的全限定名 : 包名.类名
只能在当前位置使用一次,简单方便,但是使用频繁的话建议使用import导包 - import关键字进行导包
定义语法 : import 包名.类名;
位置: 类的上面,package信息下面 - 模糊匹配
模糊匹配当前报下要使用的所有类型
只会降低编译效率,不会降低运行效率 - 静态导入 import static
导入一个类型中的静态内容
注意:项目中需要先定义包后定义类型,不要再src下面直接定义类
- 使用的位置指定类型的全限定名 : 包名.类名
1.10 面向对象的三大特性
面向对象的三大特性:封装 继承 多态
1.10.1封装 private
需要让用户知道的暴露出来,不需要让用户了解的全部隐藏起来。这就是封装。“该露的露,该藏的藏。
-
private :私有的,成员修饰符(只能修饰成员不能修饰局部)
-
私有的属性需要配合一对公共的访问方式
设置器 setter : 成员方法,为私有属性设置值
访问器 getter : 成员方法,获取私有属性的值 -
优点:提高安全性 提高代码的复用性
-
注意:私有是封装,是封装一个具体的体现
封装不仅仅是私有,类,方法,属性私有化… -
JavaBean:代表一系列的实体类|模板类
-
JavaBean规范:
- 类是公共的
- 至少提供一个空构造
- 属性私有化
- 提供一对公共的访问方式
- 重写toString()方法和equals()方法
-
测试用例
class Hide{ // 封装属性成私有属性 private String name; private String color; //无参构造器 public Hide() {} //有参构造器 public Hide(String name, String color) { this.name = name; this.color = color; } //以下为每个私有属性提供的公共的访问方式 getter 和setter public String getName() { return name; } public void setName(String name) { this.name = name; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } }
1.10.2 继承extends
子承父业,子类继承父类
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
-
为什么要使用继承
- 提高代码的复用性
-
如何使用继承
-
子类 extends 父类 ----> class Student extends Person
class Person{ } class Student extends Person{ // 继承的实现 }
-
-
注意
- 子类一旦继承父类 有权使用父类的成员(在不考虑权限的问题下)
- 子类中可以扩展子类独有的内容
- 继承是 单继承机制 —> 优势:简单 缺点:不够灵活,不利于后期维护
- 开闭原则(面向对象的设计原则之一):对修改关闭对扩展开放
- 父类|超类|基类:被继承的类 子类|派生类:继承父类的类
-
测试用例
public class TestExtends { public static void main(String[] args) { Cats c = new Cats(); c.age = 2; c.name = "爱吃花椒的喵酱"; c.eat(); // 调用父类的吃方法 c.CaiNai();// 调用本类中的方法 Dogs d = new Dogs(); d.setName("贝拉"); //调用父类中的setName()方法 String isd = d.getName(); System.out.println(isd); d.age = 4; // 调用父类中的属性 d.eat(); //调用父类中的吃方法 d.watch();// 调用本类中的方法 } } class Animal{ int age; String name; String type; public void setName(String name){ this.name = name; } public String getName(){ return this.name; } public void eat(){ System.out.println(this.name+"在吃东西"); } } class Cats extends Animal{ public void CaiNai(){ System.out.println("一只叫"+this.name+"的猫在踩奶"); } } class Dogs extends Animal{ public void watch(){ System.out.println("一只叫"+this.name+"的"+this.type+"在看门"); } }
1.10.3 多态
本质:一种事务的多种形态|多种表现形式 --> 又叫行为多态 功能多态
为了处理问题的灵活性。 就是在做之前,不要把话说死,说的模糊一点想象空间大一点,真做的时候再具体化
-
表现形式
- 父类的引用 指向子类类型的对象
-
前提
- 继承|实现(接口)
-
调用
- 实际调用子类中重写的方法 子类没有调用父类中的,父类没有则报错
- 只能调用父类中存在的成员,对子类新增内容不可见,
-
注意
- 多态如果不配合方法的重写,多态没有意义
- 父类引用指向不同的子类对象,当子类中存在重写,对功能实现方式可能不同,这是行为多态的体现,同一个功能的不同实现方式
- 正常的情况都应该为对应类型的数据赋值给对应类型的变量,除了当前满足多态时候,才能够实现对应类型的数据赋值给了其他类型的引用 要求其他类型必须为对象的父类类型
-
测试用例
public class TestPoly { public static void main(String[] args) { ; Peron p = new Teacher();// 多态 父类引用指向子类对象 p.show(); // 调用Teacher子类的show()方法 --> 输出 老师 p.sleep() // 调用Teacher子类的sleep()方法 --> 输出 老师睡觉 Peron p1 = new Student(); //p1.eat();//报错 对子类新增内容不可见 即不能调用没有重写的方法 } } class Person{ int age; public void show(){ System.out.println("人类"); } public void sleep(){ System.out.println("人类睡觉"); } } class Teacher extends Person{ String name; //对父类中show()方法重写 public void show(){ System.out.println("老师"); } //对父类中sleep()方法重写 public void sleep(){ System.out.println("老师睡觉"); } } class Student extends Person{ public void eat(){ System.out.println("学生吃饭"); } }
1.11 权限修饰符
本质:主要控制类以及类中成员的访问权限
本类 | 同包类 | 不同包下的子类 | 不同包下的不同类 | |
---|---|---|---|---|
public 公共的 | √ | √ | √ | √ |
protected 受保护的 | √ | √ | √ | |
default 缺省(默认) | √ | √ | ||
private 私有的 | √ |
- 四种都是成员修饰符 只能修饰成员不能修饰局部
- 能够就是类的 :public default
- 父类中被protected修饰的成员,在不同包下的子类中通过继承关系可以使用
1.12 super关键字
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性
-
super访问
- 访问父类的属性:super.属性名; 前提是有访问权限
- 访问父类中的方法:super.方法名();前提是有访问权限
- 访问父类中的构造器:super();前提是有访问权限
-
super 和 this 的区别
- this
- this指代当前new的对象
- 区分局部和成员的同名问题
- 能够使用在构造器的首行调用本类中的其他构造器
- super
- 指代父类对象
- 区分子类和父类的成员同名问题
- 能够在子类构造器的首行调用父类的指定构造器
- this
-
注意
- 访问构造器时:super()必须放在子类构造器中的第一行
- 在没有显示通过super调用父类构造器时,一定会在子类构
造器的入口默认调用一个无参构造 suepr() - 如果父类中没有提供无参构造,子类必须手动调用父类的
带参构造 suepr(实参列表) - 任何类的构造函数中,若是构造函数的第一行代码没有显式的调用super(…);那么Java默
认都会调用super();作为父类的初始化函数。 所以你这里的super();加不加都无所谓
-
测试用例
/** * super 和 this 的区别 * super --->指代父类对象 * this---->指代当前对象 * * 本例子输出: 父类空构造 * 子类空构造 * 二哈 * 金毛 * 边牧 */ public class TestSuper { public static void main(String[] args) { // 在子类创建对象时先创建父类对象--->先父类后子类 正所谓先有爹后有儿 所以先调用父类空构造再调用子类空构造 Sons sons = new Sons(); sons.show(); } } class Father{ String name = "边牧" ; int age; int number ; public Father(){ System.out.println("父类空构造"); } public Father(int number){ this.number = number; System.out.println("有参构造"); } } class Sons extends Father{ String name = "金毛"; public Sons(){ System.out.println("子类空构造"); } public Sons(int number){ // super(number); System.out.println("子类有参构造,一个参数"); } public void show(){ String name = "二哈"; System.out.println(name);//就近原则 System.out.println(this.name);//this指代当前对象 System.out.println(super.name);//super调用父类 } }
1.13 方法的重写
重写:顾名思义:对方法重新书写
-
前提
- 有继承关系的两个类, 父类中的方法不能满足子类需求的时候需要子类自己将该方法重新实现
-
要求
- 重写方法必须和被重写方法具有相同方法名称、参数列表和返回类型 即方法签名(注意中有解释)相同
- 如果返回值类型为基本数据类型|void,要求两个方法完全相等,只能修改要实现的内容
- 如果返回值类型为引用数据类型 : 要求子类中的重写方法返回值类型<=父类中被重写方法的返回值类型
- 子类中重写方法的权限修饰符>=父类中被重写方法的权限修饰符
-
注意
- 子类中一旦对父类中的某个功能进行重写,在调用时候回对父类的功能进行屏蔽,调用子类中重写的方法
- 被private final static 修饰的方法不能被重写
- 如果子类中存在与父类静态方法同名的方法,要求子类中的同名方法也为static才可(但是不属于重写)
- 在子类重写方法上加 @override 强制检测方法为重写方法
- 方法签名: 是方法的唯一标识 方法名+参数列表
-
重写与重载区别
- 重载
- 同一个类中的多个方法
- 方法名相同
- 参数列表不同
- 重写
- 不同的两个类
- 前提是两个类继承|接口(实现)
- 方法签名相同
- 重载
-
测试用例
public class TestOverride { public static void main(String[] args) { Zi zi = new Zi(); zi.say();//调用子类中重写的say()方法 ---> 输出:儿子说话 } } class Fu{ int age; public void say(){ System.out.println("父亲说话"); } static void sleep(){ System.out.println("睡觉"); } } class Zi extends Fu{ //对父类中的say()方法进行重写 @Override public void say() { System.out.println("儿子说话"); } //此方法不是重写!!!因为有static修饰 static void sleep(){ System.out.println("123"); } }
1.14 Object 类
Object类是所有Java类的根基类—>是所有类的父类
如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类
public class Person {
...
}
等价于
public class Person extends Object {
...
}
-
Object类中常用的方法
- toString():返回对象的字符串表现形式
- equals(): 比较两个对象是否相等 默认比较两个对象的地址值
-
注意:JavaBean规范中要求重写toString与equals方法—> 有快捷键
1.15 final 关键字
- 被final修饰的变量为常量
- 被final修饰的方法不能被重写
- 被final修饰的类不能被继承–>太监类
1.16 抽象类 abstract
被abstract修饰的类称为抽象类
被abstract修饰的方法称为抽象方法 抽象方法没有方法体且必须定义在抽象类中
-
注意
- 抽象类不能实例化,即不能被创建对象 不能使用new关键字创建它的对象
- 抽象类中可以定义抽象方法,可以定义非抽象方法,属性,构造器…
- 抽象类的使用: 通过具体子类对象调用
- 具体子类 : 重写所有的抽象方法 + 按需新增
- 抽象子类 : 按需重写抽象方法 + 按需新增
- 一个抽象方法如果一旦被重写,后续的子类中可以不再重写,可以按需重写
- 抽象方法必须要被重写,没有重写,没有方法体,不能调用
- abstract不能与static,private,final与native一起使用
-
测试用例
public class TestAbstract { public static void main(String[] args) { // Develop develop = new Develop(); // 抽象类不能被实例化 //若要调用 必须 创建具体子类对象调用 JavaScript js = new JavaScript(); js.work();// 输出:好好学习JavaScript } } //抽象类 ---> 被abstract修饰的类 abstract class Develop{ //抽象方法 ---> 被abstract 修饰的方法 abstract void work(); //抽象方法没有方法体 abstract void workTwo(); public void say(){ System.out.println("你好"); } } class JavaScript extends Develop{ // 具体子类必须对父类中的所有抽象方法重写 @Override void work(){ System.out.println("好好学习JavaScript"); } @Override void workTwo() { System.out.println("工作升级"); } public void up(){ System.out.println("升级为TypeScript"); } } abstract class Vue extends Develop{ //抽象子类 ---> 按需重写抽象方法 @Override void work() { System.out.println("Vue开发"); } } class Vue3 extends Vue{ //具体子类继承抽象子类,抽象子类又继承的抽象父类 那么该子类可以不用重写抽象子类重写过的抽象方法,但是也可以重写 @Override void workTwo() { System.out.println("Vue3开发"); } }
1.17 接口 interface
接口:特殊的抽象类 是抽象功能的集合 接口定义了开发规范 是多继承机制
-
为什么需要接口
- 接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离
- 接口是两个模块之间通信的标准,通信的规范
- 从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务
-
如何定义接口
[权限修饰符] interface 接口名 [extends 父接口1,父接口2…] { 常量定义; 抽象方法定义; } /* 1. 访问修饰符:只能是public或默认。 2. 接口名:和类名采用相同的命名机制 3. extends:接口可以多继承 4. 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。 5. 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract. */ 如:interface demo1{ void say(); }
-
接口的使用
- 子类通过 implements 来实现接口中的规范
- 接口不能创建实例,但是可用于声明引用变量类型
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public 的
- 接口中只能包含 静态常量、抽象方法 ,不能有普通属性、构造方法、普通方法、静态块
public class TestInterface { public static void main(String[] args) { Face face = new Face(); face.say(); face.hi(); System.out.println(word.PI); //word.PI = 3.15; } } interface demo1{ void say(); } interface demo2{ void hi(); } interface word extends demo1,demo2{ double PI = 3.14; } //子类实现接口 如果要继承 必须先写继承再写实现接口 class Face extends Object implements word{ @Override public void say() { System.out.println("您好"); } @Override public void hi() { System.out.println("滚咯"); } }
-
注意
Java 8发布以后,可以给接口添加新方法,但是,接口仍然可以和它的实现类保持兼容。这非常重
要,因为你开发的类库可能正在被多个开发者广泛的使用着。而Java 8之前,在类库中发布了一个
接口以后,如果在接口中添加一个新方法,那些实现了这个接口的应用使用新版本的接口就会有崩
溃的危险。(java8中也不能直接完全避免这个问题)接口中被实现的方法叫做default方法,用关键字default作为修饰符来标识。当一个类实现一个接口的时候,它可以实现已经在接口中被实现过的方法,但这不是必须的。这个类会继承default方法。这就是为什么当接口发生改变的时候,实现类不需要做改动的原因。在 java8 中的接口中不仅增加了默认方法,还增加了静态方法。定义一个或者更多个静态方法。类似于类中的静态方法,接口定义的静态方法可以独立于任何对象调用。所以,在调用静态方法时,不需要实现接口,也不需要接口的实例,也就是说和调用类的静态方法的方式类似。语法如: 接口名字.静态方法名 。实现接口的类或者子接口不会继承接口中的静态方法。static不能和default同时使用。在java8中很多接口中都增加了静态方法。
1.18 类和类之间的关系
1.18.1 继承关系
继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加
它自己的新功能的能力。在Java中继承关系通过关键字extends明确标识,在设计时一般没有争议性。在
UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
public class Person {
String name;
int age;
void move(){}
}
//继承
class Student extends Person{
void study(){}
}
class Teacher extends Person{
void teach(){}
}
1.18.2 实现关系
实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。
在Java中此类关系通过关键字implements明确标识,在设计时一般没有争议性。在UML类图设计中,实
现用一条带空心三角箭头的虚线表示,从类指向实现的接口
interface Vehicle{
void move();
}
class Ship implements Vehicle{
void move(){
System.out.println("水里移动..");
}
}
class Car implements Vehicle{
void move(){
System.out.println("陆地跑..");
}
}
1.18.3 依赖关系
简单的理解,依赖就是一个类A使用到了2,而这种使用关系是具有偶然性的、临时性的、非常弱的,但
是类B的变化会影响到类A。比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖。表现
在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类A
指向类B的带箭头虚线表示
public class Person {
public void drive(Car car){} //方法参数
}
class car{}
1.18.4 关联关系
关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存
在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的。关联可
以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,也可能是关联类
A引用了一个类型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B
的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标记
public class Person {
public Address address; //关联关系
public void setAddress (Address address){
this.address= address;
}
public Address getAddress (){
return address;
}
}
class Address{}
1.18.5 聚合关系
聚合是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系。此时整体与部分之间是可
分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比
如计算机与CPU、公司与员工的关系等,比如一个航母编队包括海空母舰、驱护舰艇、舰载飞机及核动
力攻击潜艇等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在UML类图设计中,
聚合关系以空心菱形加实线箭头表示
public class Team {
public Person person;
public Team(Person person){
this.person = person;
}
}
1.18.6 组合关系
组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚
合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着
部分的生命周期结束,比如人和人的大脑。表现在代码层面,和关联关系是一致的,只能从语义级别来
区分。在UML类图设计中,组合关系以实心菱形加实线箭头表示
public class Person {
public Head head;
public Body body;
public Person(){
head = new Head();
body = new Body();
}
}
1.19 类的设计原则
1.19.1 单一职责原则
一个类应该有且只有一个变化的原因。单一职责原则将不同的职责分离到单独的类,每一个职责都是一个变化的中心。需求变化时,将通过更改职责相关的类来体现。如果一个类拥有多于一个的职责,则多个职责耦合在一起,会有多于一个原因来导致这个类发生变化。一个职责的变化可能会影响到其他的职责,另外,把多个职责耦合在一起,影响复用性。
1.19.2 里氏代换原则
这就是要求继承是严格的is-a关系。所有引用基类的地方必须能透明地使用其子类的对象。在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。
1.19.3 依赖倒置原则
依赖倒置原则的核心就是要我们面向接口编程,理解了面向接口编程,也就理解了依赖倒置。低层模块尽量都要有抽象类或接口,或者两者都有。变量的声明类型尽量是抽象类或接口。根据标准写实现。
1.19.4 接口隔离原则
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上,通俗的讲就是需要什么就提供什么,不需要的就不要提供。
1.19.5 迪米特法则
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。也就是说,一个类应该对自己需要耦合或调用的类知道的最少,类与类之间的关系越密切,耦合度越大,那么类的变化对其耦合的类的影响也会越大,这也是我们面向设计的核心原则:低耦合,高内聚。
1.19.6 开闭原则
对修改关闭,对扩展开放。在软件的生命周期内,因为变化,升级和维护等原因需要对软件原有代码进行修改,可能会给旧代码引入错误,也有可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现。不过这要求,我们要对需求的变更有前瞻性和预见性。其实只要遵循前面5中设计模式,设计出来的软件就是符合开闭原则的。
1.20 快捷键创建JavaBean规范
-
第一步右键点击Generate 快捷键 Alt+Insert
-
第二步选择所要创建的
- Constructor—> 构造器
- Getter —>getter()方法
- Setter —>Setter()方法
- equals()和hasCode()—> equals方法和哈希
-
注意
- 使用快捷键前必须创建一个类以及类中所需的属性