面对对象
Java基础-面对对象
面向过程&面对对象1
计算机语言发展史:

例题:两数的互换

面对过程与面对对象的区别:
面向过程:
当事件比较简单的时候,利用面向过程,注重的是事件的具体的步骤/过程,注重的是过程中的具体的行为,以函数为最小单位,考虑怎么做。
面向对象:
注重找“参与者”,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
二者相辅相成,并不是对立的。解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系、方便我们分析整个系统具到微观操作,仍然使用面向过程方式来处理。
类和对象的关系:
万物万事皆是对象。
对象:具体的事物,具体的实体,具体的实例,模板下具体的产品;
类:对对象向上抽取的部分,公共的部分,形成类,类是抽象的,是一个模板。
面对对象三个阶段:
【1】面向对象分析OOA -- Object Oriented Analysis
对象:张三,王五,朱六,你,我
抽取出一个类----》人类
类里面有什么:
动词--》动态特性--》方法
名词--》静态特性--》属性
【2】面向对象设计OOD -- Object Oriented Design
先有类,再有对象:
类:人类: Person
对象: zhangsan , lisi, zhuliu
【3】面向对象编程OOP -- Object Oriented Programmingl
构造器:
创建对象的过程:
1.第一次遇到类的时候(即new后面的语句),进行类的加载(只加载一次)
2.创建对象,为这个对象在堆中开辟空间
3.为对象进行属性的初始化动作
new关键字实际上是在调用一个方法,这个方法叫构造方法(构造器)
调用构造器的时候,如果你的类中没有写构造器,那么系统会默认给你分配一个构造器,只是我们看不到罢了。
构造器和方法的区别:
1.没有方法的返回值类型
2.方法体内部不能有return语句
3.构造器的名字很特殊,必须跟类名一样
构造器的作用:
不是为了创建对象,因为在调用构造器之前,这个对象就已经创建好了,并且属性有默认的初始化的值。调用构造器的目的是给属性进行赋值操作的。
注意:
我们一般不会在空构造器中进行初始化操作,因为那样的话每个对象的属性就一样了。实际上,我们只要保证空构造器的存在就可以了,里面的东西不用写。可以自己显式的将构造器编写出来:
构造器的格式:
空构造器
[修饰符] 构造器的名字(){
}
构造器的重载
一般不会在构造器中赋值,而是在构造器重载的方法里面赋值。
如果空构造器没写,那么程序不会给你默认分配一个空构造器,直接走重载后的构造器的方法。
public 类名( String name,int age,double c){
//当形参名字和属性名字重名的时候,会出现就近原则
//在要表示对象的属性前加上this.来修饰,因为this代表的就是你创建的那个对象
this.name = name;
this.age = age;
height = c;
属性 = 值
//属性是该类中存在的成员变量,值是重载中传的参数
}
字和属性名字重名的时候,会出现就近原则
子类父类之间构造器的使用顺序步骤:

IDEA自动生成构造器的快捷键(alert+insert)下,两者的区别

面向对象---内存分析
基础内存分析1:
public class Person {
int id;
int age;
public static void main(String args[]){
Person p1= new Person);
}
}

基础内存分析2:
public class Person {
int id;
int age;
String school;
public Person (int a,int b,String c){
id=a;
age=b;
school=c;
}
public static void main(String args[]){
Person p= new Person(1,20,"海淀");
}
}

基础内存分析3:
class Person{
int id;
int age;
String school;
Person (int a,int b,String c){
id=a;
age=b;
school=c;
}
public void setAge(int a){
age=a;
}
public class Test {
public static void main(String[] args) {
Test t=new Test();
int age=40;
Person tom=new Person(1,20,"海淀");
Person jack=new Person(2,30,"朝阳");
t.change1(age);
t.change2(tom);
t.change3(jack);
system.out.println(age):
System.out.println("id:"+jack.id+ ",age:"+jack.age+",school:" +jack.school);
}
public void change1(int i){
i=3366;
}
public void change2(Person p){
p=new Person(3,22,"西城");
}
public void change3(Person p){
p.setAge(66);
}
}
类加载一次就可以了,第二次创建该对象,不需要加载该类
堆中未被指向的会被回收

this的介绍
this : 就是你创建的对象 & 或者是你调用的对象
内存分析:

this关键字用法:
( 1) this可以修饰属性:
当属性名字和形参发生重名的时候,或者属性名字和局部变量重名的时候,都会发生就近原则,所以如果我要是直接使用变量名字的话就指的是离的近的那个形参或者局部变量,这时候如果我想要表示属性的话,在前面要加上: this.修饰
如果不发生重名问题的话,实际上你要是访问属性也可以省略this.
(2) this修饰方法:
在同一个类中,方法可以互相调用,this.可以省略不写。
(3) this可以修饰构造器:
同一个类中的构造器可以相互用this调用,注意: this修饰构造器必须放在第一行

static的介绍
static修饰属性:
例题:
public class Test{
int id;
static int sid;
public static void main( string[ ] args){
Test t1= new Test();
t1.id = 10;
t1.sid = 10;
Test t2 = new Test();
t2.id = 20;
t2.sid = 20;
Test t3 = new Test();
t3.id = 30;
t3.sid = 30;
//读取属性的值:
system.out.println (t1.id);
system.out.println (t2.id);
system.out.println (t3.id);
system.out.println (tl.sid);
system.out.println (t2.sid);
system.out.println (t3.sid);
//结果:10 10 10 30 30 30
}
}
内存分析:

一般官方的推荐访问方式:可以通过类名.属性名的方式去访问:│
Test.sid = 100;
system.out.println(Test.sid);
static修饰属性的应用场景:
某些特定的数据想要在内存中共享,只有一块--》这个情况下,就可以用static修饰的属性
比如一个学校所有的学生的学校属性都是同一个,只需要:
static String school;
---
student.school = "某某大学";
static修饰属性总结:
(1在类加载的时候一起加载入方法区中的静态域中
(2) 先于对象存在
(3) 访问方式:对象名.属性名类名.属性名(推荐)
属性:
静态变量(类变量);
非静态属性(实例变量)。
static修饰方法:
-
static & public 都是修饰符,并列的没有先后顺序,谁先写后写都一样
-
在静态的属性里面不能访问非静态的属性
-
在静态方法中不能访问非静态的方法
-
在静态方法中不能使用this关键字
-
非静态的方法可以用 对象名.方法名 调用
-
静态的方法可以用 对象名.方法名调用 (不推荐);也可以用 类名.方法名(推荐)
public class Test{
int id;//属于对象特有的属性,必须有对象才能调用
static int sid;//static修饰的变量先于对象存在在内存中,跟随类加载第一时间存储了
public void a(){
//如果你能调用a方法,说明有对象存在,所以id和sid都能使用。
System.out.println(id);
System.out.println(sid);
System.out.println("-------a");
}
//1.static & public 都是修饰符,并列的没有先后顺序,谁先写后写都一样
static public void b(){
//System.out.println(this.id);//4.在静态方法中不能使用this关键字
//a();3.在静态方法中不能访问非静态的方法,存在生成上的时间差
//System.out.println(id);//2.在静态的属性里面不能访问非静态的属性
System.out.println(sid);
System.out.println("-------b");
}
public static void main(String[] args) {
//5.非静态的方法可以用对象名.方法名调用
Test test = new Test();
test.a();
//6.静态的方法可以用 对象名.方法名调用(不推荐);也可以用 类名.方法名(推荐)
Test.b();
test.b();
//在一个类中可以直接调用
b();
}
}
代码块的介绍
普通块
构造块
静态块
同步块(多线程)
public class CodeBlock {
//属性
int a;
static int sa;
//方法
public void a() {
System.out.println("这是a方法");
{
//1.普通块:限制了局部变量的作用范围
System.out.println("这是普通块");
int num = 10;
System.out.println(num);
}
//System.out.println(num);
}
public static void b() {
System.out.println("这是b方法");
}
//2.构造块:解决在方法外写代码的问题
{
System.out.println("这是构造块");
}
//3.静态块,在静态块中,只能访问静态属性,静态方法
static {
System.out.println("这是静态块");
System.out.println(sa);
b();
}
//构造器
public CodeBlock(int a) {
this.a = a;
}
public CodeBlock() {
System.out.println("这是空构造器");
}
//代码块
public static void main(String[] args) {
CodeBlock codeBlock = new CodeBlock();
codeBlock.a();
}
}
//执行结果:
/*这是静态块
0
这是b方法
这是构造块
这是空构造器
这是a方法
这是普通块
10*/
总结:
代码块执行顺序:
最先执行静态块,只在类加载的时候执行一次,所以一般以后实战写项目:创建工厂,数据库的初始化信息都放入静态块。一般用于执行一些全局性的初始化操作。
再执行构造块,(不常用)
再执行构造器,
再执行方法中的普通块。
走完父类再走子类
import的介绍
总结:
(1)使用不同包下的类要需要导包: import **..;例如: import java.util.Date;
(2)在导包以后,还想用其他包下同名的类,就必须要手动自己写所在的包
(3)同一个包下的类想使用不需要导包,可以直接使用。
(4)在 java.lang 包下的类,可以直接使用无需导包:system.out.println(Math.random());
(5)IDEA中导包快捷键:alt+enter
可以自己设置自动导包
(6)可以直接导入*:

(7)在Java中的导包没有包含和被包含的关系:
设置目录平级的格式(不是包含和被包含的显示)

静态导入:
package com.msb11;
//静态导入:
import static java.lang.Math.*;
// 导入:java.Lang 下的Math类中的所有静态的内容
public class Test {
public static void main(String[] args) {
system.out.println(random());
system.out.println(PI);
system.out.println(round(5.6));
//在静态导入下,如果类中有相同的方法名,就近原则,先走类中的方法,即system.out.println(round(5.6));输出的是1000;
public static int round(int a){
return 1000;
}
}
}
封装(重点)
概念
封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装是一种信息陷藏技术,在Java中通过关键字 private,protected和 public 实现封装。
什么是封装 ?
封装把对象的所有组成部分组合在一起,封装定义程序如何引用对象的数据,封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。适当的封装可以让程式码更容易理解和维护,也加强了程式码的安全性。
我们程序设计追求“高内聚,低藕合”。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
封装的好处 : 提高代码的安全性
实例
代码一 : 通过一个属性感受封装:
public class Girl {
//通过 private(私有化) 将属性封装起来,外部类不能直接调用该属性
private int age;
//设置,通常使用set..
public void setAge(int age){
this.age = age;
}
//读取,通常使用get..
public int getAge(){
return age;
}
}
public class Encapsulation_study {
public static void main(String[] args) {
Girl girl = new Girl();
//外界只能通过Girl里面的公共(public)方法区设置年龄以及读取年龄,更具有安全性
girl.setAge(18);
int age = girl.getAge();
System.out.println(age);
}
}
代码二 : 通过一个类的使用感受封装:
public class Student {
private String name;
private int age;
private String sex;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getSex() {
return sex;
}
public Student() {}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
//即调用当前类(Student)里面的setSex方法,参数为sex
//this.setSex(sex); & setSex(sex);
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setSex(String sex) {
if ("男".equals(sex) || "女".equals(sex)){
this.sex = sex;
}else{
this.sex = "男";
}
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
//通过set,get设置属性值
student.setName("刘**");
student.setAge(21);
student.setSex("ffjka");
System.out.println(student.getName() + "---" + student.getAge() + "---" + student.getSex());
//通过重载构造器设置属性值,不会走set方法
Student student1 = new Student("刘**",21,"男");
System.out.println(student1.getName() + "---" + student1.getAge() + "---" + student1.getSex());
//通过重载构造器设置属性值,不会走set方法,setSex里面的判断不会生效,性别输出为传的值,
// 需要将构造器中的this.sex=sex改为this.setSex(sex),即调用当前类(Student)里面的setSex方法,参数为sex.
Student student2 = new Student("刘**",21,"fsevsaf");
System.out.println(student2.getName() + "---" + student2.getAge() + "---" + student2.getSex());
}
}
继承(extends)
1、类是对对象的抽象:
举例: 荣耀50,红米K30,华为mate50 ———》类:手机类
2、继承是对类的抽象:
举例:
学生类:Student:
属性:姓名,年龄,身高,学生编号 ---- 方法:吃饭,睡觉,喊叫,学习
教师类:Teacher:
属性:姓名,年龄,身高,教师编号 ---- 方法:吃饭,睡觉,喊叫,教学
员工类:Emploee:
属性:姓名,年龄,身高,员工编号 ---- 方法:吃饭,睡觉,喊叫,工作
共同的东西:
人类:
属性:姓名,年龄,身高 ---- 方法:吃饭,睡觉,喊叫
学生类/教师类/员工类 继承自 人类
以后定义代码:(先父后子)
先定义人类: 人类 : ---》父类(基类,超类)
属性:姓名,年龄,身高 ---- 方法:吃饭,睡觉,喊叫
再定义:---》子类(派生类)
学生类: Student: 属性:学生 编号 ---- 方法:学习
教师类:Teacher: 属性:教师 编号 ---- 方法:教学
员工类: Emploee: 属性:员工 编号 ---- 方法:工作
抽取共有的属性和方法,放在父类之中,子类直接继承父类即可。
我们的继承关系是在合理的范围中进行的抽取,比如狗狗也会吃饭,睡觉,喊叫,但是显然不能使用在人类在使用继承,违背了基本的对类的概念化。
提高代码的复用性
需要注意的点:
父类 private 修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用但是提供了间接调用的方式,可以间接调用(使用 set)。
举例:
//父类
public class Person {
private String name;
private int age;
private String height;
public Person() {
}
public Person(String name, int age, String height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
public void eat(){
System.out.println("吃饭...");
}
public void sleep(){
System.out.println("睡觉...");
}
}
//子类
public class Student extends Person{
private int sno;
public Student(int sno) {
this.sno = sno;
}
public Student() {}
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public void study(){
System.out.println("学生可以学习");
}
}
//测试
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("刘**");
student.setAge(21);
student.setHeight("180");
student.setSno(2116); System.out.println(student.getName()+student.getAge()+student.getHeight()+student.getSno());
student.study();
student.eat();
student.sleep();
}
}
内存分析
继承的东西该用的能用的用即可

总结:
(1)继承关系∶父类/基类/超类 子类/派生类
子类继承父类一定在合理的范围进行继承的子类extends父类
(2)继承的好处:
1.提高了代码的复用性,父类定义的内容,子类可以直接拿过来用就可以了,不用代码上反复重复定义了
2.便于代码的扩展
3.为了以后多态的使用。是多态的前提。
(3)父类private修饰的内容,子类也继承过来了。
(4)一个父类可以有多个子类。
(5)一个子类只能有一个直接父类。但是可以间接的继承自其它类。
(6)继承具有传递性:
Student --》继承自 Person ---》继承自 Object类是所有类的根基父类。
所有的类都直接或者间接的继承自Object。
权限修饰符
四种修饰符权限
| 是否能直接访问 | 同一个类 | 同一个包 | 子类 | 所有类 |
|---|---|---|---|---|
| private(私有) | 是 | 否 | 否 | 否 |
| default(缺省值) | 是 | 是 | 否 | 否 |
| protected(保护) | 是 | 是 | 是 | 否 |
| public(公共) | 是 | 是 | 是 | 是 |
缺省值 = 系统默认值 比如:(default) int age default 默认不写
权限由上至下逐渐变大
总结:
属性方法的修饰符有以上四种,通常类只用default和public两种修饰符。
类中的一般属性用private修饰,方法则用public修饰。
方法的重写
发生在子类和父类中,子类对父类的方法不满意的时候,对父类的方法重写调用的时候会走在子类中的重写后的方法,不会走父类的方法。

方法的重载
一个类中,多个方法的方法名相同,但是参数(包括类型和数量)不相同,构成了方法的重载。
本质其实是:方法不同,只是恰巧名字相同。
方法的重载只和方法名与参数列表有关,与修饰符,返回类型无关。
重写和重载的区别
| 英文 | 位置 | 修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 | 方法体 | |
|---|---|---|---|---|---|---|---|---|
| 重写 | overwrite | 子与父类 | 父类的权限小于子类的权限 | 父类的返回值大于子类 | 必须相同 | 必须相同 | 小于等于 | 不同 |
| 重载 | overload | 同一个类 | 无关 | 无关 | 必须相同 | 必须不同 | 无关 | 不同 |
两者没有任何关系
super的介绍
必须在子类与父类中使用
super修饰属性与方法
super指的是父类的属性或者方法,如果要调用子类或者父类的属性及方法,使用"super."即可,如果子类中没有相同的属性或者方法,"super."可以省去。如果有并且实际上需要调用父类的属性或者方法,那么需要加上"super." 。
super修饰构造器
其实我们平时写的构造器的第一行都有: super()
作用:调用父类的空构造器,只是我们一般都省略不写

如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了

在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存:因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行:

改正:

IDEA自动生成构造器的快捷键(alert+insert)下,两者的区别

子类父类之间构造器的使用顺序步骤:

Object类
所有的类直接或者间接的继承自Object类
所有类都直接或间接的继承自Object类,Object类是所有Java类的根基类。
也就意味着所有的Java对象都拥有Object类的属性和方法。
如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
1、toString方法
返回该数据的字符串表示
Object中:实际执行后返回的值:getClass().getName() + '@’+ Integer.toHexString(hashCode())
getClass().getName():得到类名。全限定路径:包名+类名的完整表示
@:在
hashCode():将这个对象在堆中的地址进行哈希算法,返回一个码:哈希码,将这个哈希码传入到Interger.toString(哈希码)中,返回一个字符串,这个字符串是一个16进制的数对应的字符串。
过程:对象-->堆中分配地址-->进行哈希操作-->得到哈希码-->转成16进制-->String
如果我们想知道显示的信息,那么需要将String转化为可显示的数据,将toString进行重写
public String toString(){
return "这是一个student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height;
}
同时可以使用IDEA中的快捷键自动生成toString的重写方法,不需要手动输入,如果想有自己想要的显示数据格式,可以自己重写。
2、equals方法
例子讲解:
public class Phone {
String name;
double price;
int year;
public Phone() {
}
public Phone(String name, double price, int year) {
this.name = name;
this.price = price;
this.year = year;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "Phone{" +
"name='" + name + '\'' +
", price=" + price +
", year=" + year +
'}';
}
//对equals方法进行重写:
public boolean equals(Object obj){
//将obj转换为Phone类型:
Phone other = (Phone)obj;//具体原因涉及多态,之后学习到即能体会
if (this.getName()== other.getName()&&this.getPrice()== other.getPrice()&&this.getYear()== other.getYear()){
return true;
}else
return false;
}
}
public class Test {
public static void main(String[] args) {
Phone phone_1 = new Phone("phone12",8888,2021);
Phone phone_2 = new Phone("phone12",8888,2021);
System.out.println(phone_1==phone_2);
//两个对象在堆中分配了两个不同的地址,所以所比较的地址不同,即返回false
System.out.println(phone_1.equals(phone_2));
//重写equals后,比较的则是两个对象中的值,此时是相同的,所以返回值是true
//未重写equlas之前返回值是flase,因为equals底层比较的实际上还是使用"==",而显然两个对象的地址不同,所以返回值是false
}
}
总结
equals作:这个方法提供了对对象的内容是否相等的一个比较方式,对象的内容指的就是属性。
父类Object提供的equals就是在比较==地址,没有实际的意义,我们一般不会直接使用父类提供的方法,而是在子类中对这个方法进行重写。
== :对于引用类的对象,比较的是地址; 对于正常的数据类型,比较的是值
instance of的使用
比较两个类是否为同一种
a instance of b -----> a是b的实例。
假如在上方代码中还存在一个其他类Cat,new一个cat后,再用equals比较cat 与 phone_1,发现报如下错误:
Cat cat = new Cat();
System.out.println(phone_1.equals(cat));
//对应的异常:ClassCastException
如果要解决这个异常,进行比较,需要用:instance of
//对equals方法进行重写:
public boolean equals(Object obj) {
if (obj instanceof Phone) {//判断是否属于phone类型的对象
//将obj转换为Phone类型:
Phone other = (Phone) obj;//具体原因涉及多态,之后学习到即能体会
if (this.getName() == other.getName() && this.getPrice() == other.getPrice() && this.getYear() == other.getYear()) {
return true;
}
return false;
}
return false;
}
类与类之间的关系
类与类可以产生的关系:
(1)依赖关系:将一个类作为另一个类的方法的形参
(2)关联关系:将一个类作为另一个类的属性(两者比较平等)
(3)继承
(4)实现
(5)
聚合关系(包含于被包含的关系,例如公司与员工),组合关系(很强的关系,两者不可或缺,例如大脑与身体)在代码层面,都是关联关系,只能从语义级别来区分。
多态
多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
多态的应用场合:
(1)父类当做方法的形参,传入具体的子类的对象
(2)父类当做方法的返回值,返回的是具体的子类的对象
(3)接口当做方法的形参,传入具体的实现类的对象
(4)接口当做
方法的返回值,返回的是具体的实现类的对象
实例:
public class Cat extends Animal {
public void shout(){
System.out.println("喵~");
}
public void scratch(){
System.out.println("挠~");
}
}
public class Dog extends Animal{
public void shout(){
System.out.println("汪~");
}
public void guard(){
System.out.println("是小狗,我可以保护主人");
}
}
public class Animal {
public void shout() {
System.out.println("我是小动物,我可以叫~");
}
}
public class Girl {
/*public void play(Cat cat){
cat.shout();
}
public void play(Dog dog){
dog.shout();
}*/
//使用一个play实现对多种动物类的操作
public void play(Animal animal){
animal.shout();
}
}
public class Test {
public static void main(String[] args) {
Girl girl = new Girl();
/* Cat cat = new Cat();
girl.play(cat);
Dog dog = new Dog();
girl.play(dog);*/
Cat cat = new Cat();
Dog dog = new Dog();
/*没有具体的动物玩耍时:
Animal animal = new Animal();
girl.play(animal);*/
//当需要具体的动物玩耍时,直接另animal等于动物某个类,
// 而不需要在每个Girl类中写上许多的play重载方法:
Animal animal = cat;
//等同于:Animal animal1 = new Cat();
girl.play(animal);
}
}
总结:
先有父类,再有子类 --》 继承
先有子类,再抽取父类 --》 泛化
多态就是多种状态 : 同一个行为(play),不同的子类(cat dog)表现出来不同的形态(喵~ 汪~)。
多态指的就是同一个方法调用,然后由于对象不同会产生不同的行为。
多态的好处:
为了提高代码的扩展性,符合面向对象的设计原则:开闭原则。
开闭原则 : 指的就是扩展是开放的,修改是关闭的。
多态可以提高扩展性,但是没有达到最好,之后学习的反射更好
多态的要素:
一,继承 : Cat extends Animal ; Dog extends Animal
二,重写 : 子类对父类的方法shout()重写
三,父类引用指向子类对象 :
Animal animal = new Cat();
Animal animal = new Cat();
=左侧 : 编译期的类型
=右侧 : 运行期的类型
上面的代码,也是多态的一种非常常见的应用场合:父类当方法的形参,然后传入的是具体的子类的对象,然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态。
多态的内存分析
animal中又属性age,方法shout
pig中有属性weight,重写的shout方法,以及eat方法

向上转型与向下转型

转型:

转型后:

同理,equals方法也是这样的(父类作为形参,子类作为实参)

简单工厂设计模式
不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。
简单工厂模式的基本要求是
(1)定义一个static方法,通过类名直接调用;
(2)令返回值类型是父类类型,返回的可以是其任意子类类型;
(3)传入一个字符串类型的参数,工厂根据参数创建对应的子类产品。
实例:
宠物店相当于工厂类


final的介绍
1、final修饰变量
final修饰引用数据类型,那么地址值不可以改变
八大数据类型:
final int A = 10;
A = 20;//报错,不能修改
A的值不能改变,变成了一个字符常量,约定俗称的规定:名字大写
引用数据类型:
final Dog d = new Dog();//final修饰引用数据类型,那么地址值不可以改变
//d = new Dog(); ---> 地址值不可以改变
d.age = 10;
d.weight = 13;
//d对象的属性可以更改
//第3种情况;
final Dog d2 = new Dog();
a(d2);
//第4种情况:
b(d2);
public static void a(Dog d){
d = new Dog();
}
public static void b(final Dog d){//dfinal修饰,指向不可以改变
//d = new Dog ( );
}
2、final修饰方法
在一个类中,只能有一个被public修饰的类
final修饰方法,那么这个方法不可以被该类的子类重写
public class Person {
final public void eat() {//final修饰方法,那么这个方法不可以被该类的子类重写
System.out.println("我可以吃饭");
}
}
class Student extends Person {//此处会报错,因为被final修饰的方法不可以被该类的子类重写
@Override
public void eat(){
super.eat();
}
}
3、final修饰类
被final修饰的类代表没有子类,不能被继承,一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了
public final class Person {
public void eat() {
System.out.println("我可以吃饭");
}
}
class Student extends Person {//被final修饰的类代表没有子类,不能被继承,一旦一个类被final修饰,那么里面的方法也没有必要用final修饰了
@Override
public void eat(){
super.eat();
}
}
案例:
JDK提供的Math
(1) 使用Math类的时候无需导包,直接使用即可:
package java.lang;
(2) Math类没有子类,不能被其他类继承了
public final class Math{}
(3) 里面的属性全部被final修饰,方法也是被final修饰的,只是省略不写了
原因:子类没有必要进行重写。
(4)外界不可以创建对象
Math m = new Math();
//Don't let anyone instantiate this cLass.
private Math() {}
(5)发现Math类中的所有的屈性,方法都被static修饰,那么不用创建对象去调用,只能通过 类名.属性名类名.方法名 去调用
抽象类
案例:
//4.一个类中如果有方法是抽象方法,那么这个类也要变成一个抽象类。
//5.一个抽象类中可以有0~n个抽象方法
public abstract class Person {
//1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用
public void eat(){
system.out.println("一顿不吃饿得慌");
}
//2.在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写。
//3.一个方法的方法体去掉,然后被abstract修饰,那么这个方法就变成了一个抽象方法
public abstract void say();
public abstract void sleep();
}
//6.抽象类可以被其他类继承:
//7.一个类继承一个抽象类,那么这个类可以变成抽象类
//8.一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法
//9.子类继承抽象类,就必须重写全部的抽象方法
//10.子类如果没有全部重写父类的抽象方法,需要将子类定义为抽象类,但是通常不这样
class Student extends Person{
@Override
public void say(){
system.out.print1n("我是东北人,我喜欢说东北话。。");
}
@Override
public void sleep(){
system.out.print1n("我是东北人,我喜欢睡炕。。");
}
}
class Demo{
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//11.创建抽象类的对象:-->抽象类不可以创建对象
// Person p = new Person();
//12.创建子类对象:
student s = new student();
s.sleep( );
s.say();
//13.多态的写法:父类引用指向了类对象:
Person p = new student( );
p.say();
p.sleep();
}
}
面试题:
(1)抽象类不能创建对象,那么抽象类中是否有构造器?
抽象类中一定有构造器。构造器的作用给子类初始化对象的时候要先super调用父类的构造器。
(2)抽象类是否可以被final修饰?
不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类。
接口(interface)
案例:
//1.类是类,接口是接口,它们是同一层次的概念。
//2.接口中没有构造器
//3.按口如何声明: interface
//4.在JDK1.8之前,接口中只有两部分内容:
//(1)常量:固定修饰符:public static final
//(2)抽象方法:固定修饰符:public abstract
//注意:修饰符可以省路不写,IDE会帮你自动补全,但是初学者建议写上,防止遗忘。
public interface Interface_study {
//常量:
/*public static final*/ int NUM = 10;
//抽象方法:
/*public abstract*/ void a();
/*public abstract*/ void b(int num);
/*public abstract*/ int c( String name);
}
/*
5.类和接口的关系是什么?实现关系类实现接口:
6.一旦实现一个接口,那么实现类要重写接口中的全部的抽象方法:
7.如果没有全部重写,可以将该类写成抽象类
8.java只有单继承,java还有多实现
一个类继承其他类只能直接继承一个父类
但是实现类实现接口的话,可以实现多个接口
9.先继承,再实现:Student extends Person implements Interface_study,Interface_study02
*/
public interface Interface_study02 {
void d();
}
class Student extends Person implements Interface_study,Interface_study02{
@Override
public void a() {
System.out.println("---1");
}
@Override
public void b(int num){
System.out.println("---2");
}
@Override
public int c(String name){
return 100;
}
@Override
public void d(){
System.out.println("---3");
}
public static void main(String[] args) {
//10.接口不能创建对象:
//Interface_study t = new Interface_study();
Interface_study t = new Student();//接口指向实现类 --》 多态
//11.接口中的常量如何使用访问
System.out.println(Interface_study.NUM);
System.out.println(Student.NUM);
Student s = new Student();
System.out.println(s.NUM);
Interface_study t2 = new Student();
System.out.println(t2.NUM);
}
}
接口的作用
定义规则,只是跟抽象类不同地方在哪?
它是接口 不是类。接口定义好规则之后,实现类 负责实现 即可。
继承与实现的区别:
继承:子类对父类的继承
实现:实现类对接口的实现
例如:
案例一:
手机是不是照相机
继承:手机 extends 照相机 “is-a”的关系 ,手机是一个照相机
上面的写法不好:
实现:手机 implements 拍照功能 “ has-a”的关系 ,手机具备照相的能力
继承是用现有的工具,实现是自己做工具,但是得按照接口的规则做
接口和抽象类的区别:
抽象类:
1、抽象类使用abstract修饰;
2、抽象类不能实例化,即不能使用new关键字来实例化对象;
3、含有抽象方法(使用abstract关键字修饰的方法)的类是抽象类,必须使用abstract关键字修饰;
4、抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;
5、如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类;
6、抽象类中的抽象方法没有方法体的具体实现;
接口:
1、接口使用interface修饰;
2、接口不能被实例化;
3、一个类只能继承一个类,但是可以实现多个接口;
4、接口中方法均为抽象方法;
5、接口中不能包含实例域或静态方法(静态方法必须实现,接口中方法是抽象方法,不能实现)
在JDK1.8之前
接口中只有两部分内容:
(1)常量:固定修饰符:public static final
(2)抽象方法:固定修饰符:public abstract
在JDK1.8之后
新增非抽象方法:
(1)被public default修饰的非抽象方法:
注意1: default修饰符必须要加上,否则出错
注意2∶实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加,否则出错。
public interface Interface_study {
//常量:
/*public static final*/ int NUM = 10;
//抽象方法:
/*public abstract*/ void a();
/*public abstract*/ void b(int num);
/*public abstract*/ int c( String name);
//非抽象方法:
public default void d(){};
}
public class Bird implements Interface_study{
@Override
public void a() {
d();//可以
//super.d();//不可以
Interface_study.super.d();//可以
}
@Override
public void b(int num) {
}
@Override
public int c(String name) {
return 0;
}
public static void main(String[] args) {
}
}
(2)静态方法:
public interface Interface03 {
public static final int NUM = 10;
public abstract void a();
public static void d(){
System.out.println("---Interface03中的static方法---");
}
}
public class Demo implements Interface03{
@Override
public void a() {
}
public static void d(){
System.out.println("---Demo中的static方法---");
}
}
class A{
public static void main(String[] args) {
Demo d = new Demo();
d.d();
Demo.d();
Interface03.d();
}
}
/*---Demo中的static方法---
---Demo中的static方法---
---Interface03中的static方法---*/
疑问:为何在接口中加入非抽象方法
如果接口中只能定义抽象方法,如果我要修改接口中的内容,那么对实现类的影响太大了,所有的实现类都会受到影响。现在在接口中加入非抽象方法,对实现类没有影响,想调用即能调用。
内部类
成员内部类
/*
1.类的组成:属性,方法,构造器,代码块(普通块,静态块,构造块,同步块),内部类
2.一个类Testouter的内部的类SubTest叫内部类,内部类:SubTest 外部类:Testouter
3.内部类:成员内部类(静态的,非静态的)和 局部内部类(位置:方法内,块内,构造器内)
4.成员内部类:里面属性,方法,构造器等
修饰符:private, default, protect,public, final, abstract
*/
public class TestOuter {
//非静态的成员内部类:
public class D {
int age = 20;
String name;
public void method() {
//5.内部类可以访问外部类的内容
/*
System.out.println(age);
a();
*/
//8.内部类和外部类重名的时候,如何进行调用:
int age = 30;
// 8. 内部类和外部类属性重名的时候,如何进行调用:
System.out.println(age);//30
System.out.println(this.age);//20
System.out.println(TestOuter.this.age);//10
}
}
//静态的成员内部类:
static class E{
public void method(){
//6.静态内部类中只能访问外部类中被static修饰的内容
/*System.out.println(age);
a();*/
}
}
//属性:
int age = 10;
//方法:
public void a() {
System.out.println("这是a方法");
{
System.out.println("这是一个普通代码块");
class B {
}
}
class A {
}
//7.外部类想要访问内部类的东西,需要创建内部类的对象,然后进行调用
D d = new D();
System.out.println(d.name);
d.method();
}
static {
System.out.println("这是静态块");
}
{
System.out.println("这是构造块");
}
//构造器:
public TestOuter() {
class C {
}
}
public TestOuter(int age) {
this.age = age;
}
}
class Demo{
public static void main(String[] args) {
//创建外部类的对象:
TestOuter to = new TestOuter();
to.a();
//创建内部类的对象:
//静态的成员内部类创建对象:
TestOuter.E e = new TestOuter.E();
//非静态的成员内部类的创建对象:
//错误:Testouter.D d = new TeeInputStream.D();
TestOuter t = new TestOuter();
TestOuter.D d = t.new D();
}
}
局部内部类
public class TestOuter2 {
//1.在局部内部类在中访问的变量必须是final修饰的
public void method() {
final int num = 10;
class A {
public void a() {
//num = 20;
System.out.println(num);
}
}
}
//2.如果类B在整个项目中只使用了一次,那么就没有必要单独创建一个类,使用内部类就可以了
public Comparable method2() {
class B implements Comparable {
@Override
public int compareTo(Object o) {
return 520;
}
}
return new B();
}
//3.匿名内部类
public Comparable method3() {
return new Comparable() {
@Override
public int compareTo(Object o) {
return 520;
}
};
}
public void test() {
Comparable com = new Comparable() {
@Override
public int compareTo(Object o) {
return 520;
}
};
System.out.println(com.compareTo("abc"));
}
}
面对对象的小项目:


数据类型的执行级别和默认值
执行级别
byte , short , char --> int --> long --> float ---> double
同理,在方法传值的同时也遵循这个顺序。
数据类型默认值

局部变量必须初始化值


浙公网安备 33010602011771号