Java面向对象
面向对象研究面向对象
什么是对象
万事万物皆为对象。没有什么是不能面向对象的。具体点说我们将某个事物的特征和行为进行共性的提取就构成了对象!比如汽车有品牌、颜色、价格这些特征。有启动、加速、停止等行为。 我们将其共性部分提取出来发在一个类中进行声明就构成了一个汽车对象;
public class Car {
//汽车的共有属性(特征)
String brand;//品牌
String color;//颜色
double price;//价格
//汽车的共有方法(行为)
public void start(){
System.out.println("车启动的方法!");
}
public void stop(){
System.out.println("车停止的方法!");
}
public void speed(){
System.out.println("车加速的方法!");
}
}
为什么要面向对象
我们现在有个需求 声明全世界不同品牌的车 并执行不同品牌汽车的启动方法 你会怎么做呢? 声明一个数组?声明多个汽车变量?
显然这样书写会使主程序过于庞杂,不利于代码书写,不利于代码阅读,不利于代码维护…(* ̄0 ̄)ノ
所以最直接的优势便体现出来使用面向对象的方式实现了更快和更便捷的开发与维护过程。
做个对象(类的抽象)
为了更好的理解面向对象,我们来面向对象研究面向对象,做一个简陋的后宫模拟器!你来当皇帝!
首先后宫要有佳丽三千!!!!!!
我们简单的提取一些后宫佳丽的共同特征(姓名、身高、体重、颜值、位份等等 )、共同行为(赏花、吃饭、侍寝等等)将这些放在类中
语法:
public class 类名 {
//定义属性部分
属性1的类型属性1;
属性2的类型属性2;
…
属性n的类型属性n;
//定义方法部分
方法1;
方法2;
…
方法m;
}
做个一个后宫佳丽的对象 新建WifeConcubine类用于存放后宫佳丽的属性、方法
package com.qingsu.personnel;
public class WifeConcubine {
//我们将对象的特征称之为对象的属性 将对象的行为称之为对象的方法
//后宫佳丽的共有属性 (对象的属性)
String name;//姓名
int age;//年龄
double height; //身高
String weight;//体重
String position;//位份
int appearance;//颜值
String hobby;//爱好
String familyBackground;//家庭背景
String address;//住址
//后宫佳丽的共有方法 (对象的方法)
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
}
//和皇上一起吃饭
public void eat() {
System.out.println("这是一个吃饭的行为");
}
//跳舞
public void dancing() {
System.out.println("这是一个跳舞的方法");
}
//侍寝
public void waiter() {
System.out.println("这是一个侍寝的方法");
}
}
声明一个对象
我们简单的做了一个后宫妻妾的对象那么怎么使用这个对象呢?
我们新建一个Simulation类进行后宫的模拟
首先我们要声明一个对象
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine();//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
对对象种的属性赋值
•使用“.”来操作对象
•调用类的属性: 对象名.属性
•调用类的方法: 对象名.方法名()
//对李秀影对象内的属性进行赋值
xiuying.name="李秀影 "; //对名字进行赋值
xiuying.age=19; //对年龄进行赋值
xiuying.height=1.66;//对身高进行赋值、
xiuying.position="美人";//对位份进行赋值
xiuying.appearance = 7; //颜值
xiuying.hobby="弹琴"; //爱好
xiuying.familyBackground="父亲为户部侍郎"; //家世
xiuying.address="钟粹宫"; //地址
System.out.println(xiuying.name+"是后宫模拟器的一员,年龄"+xiuying.age+"岁,身高"+xiuying.height+",在后宫的位份是"+xiuying.position+",她的爱好是"+xiuying.hobby+",她的家世"+xiuying.familyBackground+",她的住址是"+xiuying.address);
//继续创建一个嫔妃对象 季凌菲
WifeConcubine lingfei = new WifeConcubine();
lingfei.name="季凌菲 "; //对名字进行赋值
lingfei.age=21; //对年龄进行赋值
lingfei.height=1.71;//对身高进行赋值
lingfei.position="妃";//对位份进行赋值
lingfei.appearance = 7; //颜值
lingfei.hobby="舞蹈";
lingfei.familyBackground="父亲为兵部尚书";
lingfei.address="景仁宫";
System.out.println(lingfei.name+"是后宫模拟器的一员,年龄"+lingfei.age+"岁,身高"+lingfei.height+",在后宫的位份是"+lingfei.position+",她的爱好是"+lingfei.hobby+",她的家世"+lingfei.familyBackground+",她的住址是"+lingfei.address);
控制台输出
调用对象的方法
对李秀影的方法进行调用
xiuying.dancing();
xiuying.eat();
xiuying.waiter();
xiuying.flowerViewing();
//完整代码
package com.qingsu.personnel;
public class Simulation {
public static void main(String[] args) {
//创建一个嫔妃对象 李秀影
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine();//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
//对李秀影对象内的属性进行赋值
xiuying.name="李秀影 "; //对名字进行赋值
xiuying.age=19; //对年龄进行赋值
xiuying.height=1.66;//对身高进行赋值、
xiuying.position="美人";//对位份进行赋值
xiuying.appearance = 7; //颜值
xiuying.hobby="弹琴"; //爱好
xiuying.familyBackground="父亲为户部侍郎"; //家世
xiuying.address="钟粹宫";
System.out.println(xiuying.name+"是后宫模拟器的一员,年龄"+xiuying.age+"岁,身高"+xiuying.height+",在后宫的位份是"+xiuying.position+",她的爱好是"+xiuying.hobby+",她的家世"+xiuying.familyBackground+",她的住址是"+xiuying.address);
//继续创建一个嫔妃对象 季凌菲
WifeConcubine lingfei = new WifeConcubine();
lingfei.name="季凌菲 "; //对名字进行赋值
lingfei.age=21; //对年龄进行赋值
lingfei.height=1.71;//对身高进行赋值
lingfei.position="妃";//对位份进行赋值
lingfei.appearance = 7; //颜值
lingfei.hobby="舞蹈";
lingfei.familyBackground="父亲为兵部尚书";
lingfei.address="景仁宫";
System.out.println(lingfei.name+"是后宫模拟器的一员,年龄"+lingfei.age+"岁,身高"+lingfei.height+",在后宫的位份是"+lingfei.position+",她的爱好是"+lingfei.hobby+",她的家世"+lingfei.familyBackground+",她的住址是"+lingfei.address);
//调用对象的方法 实现对象的行为
xiuying.dancing();
xiuying.eat();
xiuying.waiter();
xiuying.flowerViewing();
}
}
控制台输出
基本数据类型的参数传递和引用数据类型的参数传递异同
首先我们要了解基本数据类型和引用数据类型在内存中的存储方式是不同的。基本数据类型的值直接存储在栈中 而引用数据类型在栈中存储的是内存地址 真正的数据存储在堆中
有不了解的可以看一下下面的文章中基本数据类型和引用数据类型的异同
点击查看基本数据类型和引用数据类型的异同
基本数据类型的参数传递:在进行参数传递时传递的参数值 而不是参数本身
示例:
package com.qingsu.basis;
public class PassTest {
public void change(int x) {//形参
System.out.println("x=="+x); //输出传入的值200
x=100; //修改值x为100
System.out.println("x=="+x); //输出的值应为100
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
//基本类型参数传递案例 基本类型当实参传递的时候注意 他传递的是值 不是变量本身
PassTest test1 = new PassTest();
int x=200; //实参x=200
test1.change(x); //传递一个200的值给形参
System.out.println(x);//输出的值应为实参x的值
}
}
控制台输出
由控制台输出可知 基本数据类型在参数传递的过程中传递的时参数的值 在方法执行过程中形参的改变不会影响实参本身
引用数据类型:引用数据类型在进行参数传递时传递的是参数的内存地址 在方法执行过程中对数据的修改会影响实参本身的数据
示例
package com.qingsu.basis;
public class PassTestTwo {
int x;
public void changeTwo(PassTestTwo test2) {
System.out.println("test2==="+test2); //打印输出形参test2的内存地址
test2.x=100; //修改test2对象中x的值 为100
}
//引用类型的参数传递 传递的是对象本身 对象中属性发生改变 会影响对象属性
public static void main(String[] args) {
PassTestTwo test2 = new PassTestTwo();
System.out.println("test2==="+test2);//打印输出形参test2的内存地址
test2.x=200; //修改test2对象中x的值 为100
System.out.println("x==="+test2.x);//打印test2中x属性的值
test2.changeTwo(test2);//调用changeTwo方法
System.out.println("x==="+test2.x); //再次打印test2中x属性的值
}
}
控制台输出
由控制台输出可知 方法外部和方法内部参数的内存地址是一样的 当我们在方法中对引用数据类型的数据进行修改时会影响到实参本身的数据
注意事项
➥对象是类实例化出来的,对象中含有类的属性,类是对象的抽象
➥每个对象都是相互独立互不影响的
➥类体中属性与方法是同一级别的
➥类体中不能直接出现可执行性语句:
➧循环(for、wile等)、分支(if、switch)不能出现
➧ 赋值语句不能出现
➧ 打印语句不能出现
构造方法
是什么?
构造方法是一种特殊的方法,它是一个与类同名的方法。对象的创建就是通过构造方法来完成,其功能主要是完成对象的初始化。当类实例化一个对象时会自动调用构造方法。构造方法和其他方法一样也可以重载。
怎么用?
声明格式:方法名与类名(必须)相同,没有返回值类型,连void都没有 没有具体的返回值
格式: 修饰符(public) 类名(参数列表){}
示例:
package com.qingsu.basis;
public class ConstructionMethod {
public ConstructionMethod() {
System.out.println("我是一个构造方法 我在实列化对象时会被自动调用");
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
ConstructionMethod constructionMethod = new ConstructionMethod();
}
}
控制台输出
有什么用?
1.初始化对象
现在我们有一个需求 我们希望在实例化后宫对象时输出一个对象已被创建 此时我们需要在WifeConcubine类中书写一个构造方法
public WifeConcubine() {
System.out.println(“对象已被创建”);
}
package com.qingsu.personnel;
public class WifeConcubine {
//我们将对象的特征称之为对象的属性 将对象的行为称之为对象的方法
//后宫佳丽的共有属性 (对象的属性)
String name;//姓名
int age;//年龄
double height; //身高
String weight;//体重
String position;//位份
int appearance;//颜值
String hobby;//爱好
String familyBackground;//家庭背景
String address;//住址
//后宫佳丽的共有方法 (对象的方法)
//无参构造
public WifeConcubine() {
System.out.println("对象已被创建");
}
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
}
//和皇上一起吃饭
public void eat() {
System.out.println("这是一个吃饭的行为");
}
//跳舞
public void dancing() {
System.out.println("这是一个跳舞的方法");
}
//侍寝
public void waiter() {
System.out.println("这是一个侍寝的方法");
}
}
修改后 Simulation控制台输出
2.初始化数据
现在我们有一个需求 我们希望在实例化后宫对象时对其属性进行赋值 并输出一个姓名+对象已被创建 此时我们需要在WifeConcubine类中书写一个有参构造方法
public WifeConcubine(String name) {
System.out.println(name+“对象已被创建”);
}
完整代码:WifeConcubine类
package com.qingsu.personnel;
public class WifeConcubine {
//我们将对象的特征称之为对象的属性 将对象的行为称之为对象的方法
//后宫佳丽的共有属性 (对象的属性)
String name;//姓名
int age;//年龄
double height; //身高
String weight;//体重
String position;//位份
int appearance;//颜值
String hobby;//爱好
String familyBackground;//家庭背景
String address;//住址
//后宫佳丽的共有方法 (对象的方法)
public WifeConcubine() {
System.out.println("对象已被创建");
}
public WifeConcubine(String name) {
System.out.println(name+"对象已被创建");
}
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
}
//和皇上一起吃饭
public void eat() {
System.out.println("这是一个吃饭的行为");
}
//跳舞
public void dancing() {
System.out.println("这是一个跳舞的方法");
}
//侍寝
public void waiter() {
System.out.println("这是一个侍寝的方法");
}
}
调用有参构造
WifeConcubine lingfei = new WifeConcubine(“季凌菲”);//直接对姓名进行赋初值
package com.qingsu.personnel;
public class Simulation {
public static void main(String[] args) {
//创建一个嫔妃对象 李秀影
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine("李秀影");//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
//对李秀影对象内的属性进行赋值
xiuying.age=19; //对年龄进行赋值
xiuying.height=1.66;//对身高进行赋值、
xiuying.position="美人";//对位份进行赋值
xiuying.appearance = 7; //颜值
xiuying.hobby="弹琴"; //爱好
xiuying.familyBackground="父亲为户部侍郎"; //家世
xiuying.address="钟粹宫"; //
System.out.println(xiuying.name+"是后宫模拟器的一员,年龄"+xiuying.age+"岁,身高"+xiuying.height+",在后宫的位份是"+xiuying.position+",她的爱好是"+xiuying.hobby+",她的家世"+xiuying.familyBackground+",她的住址是"+xiuying.address);
//继续创建一个嫔妃对象 季凌菲
WifeConcubine lingfei = new WifeConcubine("季凌菲");//直接对姓名进行赋初值
lingfei.age=21; //对年龄进行赋值
lingfei.height=1.71;//对身高进行赋值
lingfei.position="妃";//对位份进行赋值
lingfei.appearance = 7; //颜值
lingfei.hobby="舞蹈";
lingfei.familyBackground="父亲为兵部尚书";
lingfei.address="景仁宫"; //
System.out.println(lingfei.name+"是后宫模拟器的一员,年龄"+lingfei.age+"岁,身高"+lingfei.height+",在后宫的位份是"+lingfei.position+",她的爱好是"+lingfei.hobby+",她的家世"+lingfei.familyBackground+",她的住址是"+lingfei.address);
//调用对象的方法 实现对象的行为
xiuying.dancing();
xiuying.eat();
xiuying.waiter();
xiuying.flowerViewing();
}
}
控制台输出结果
在控制台上看到 有参构造被成功调用并赋值成功 但是对象内的属性name并没有值 我们需要一个解决方法来将构造方法的参数传递给对象 解决方法请看下面的this关键字介绍
注意: 当我们不写构造函数时 系统会使用默认的构造函数 什么也不做 当我们有有参构造函数而没有无参构造函数时 我们使用无参的对象创建会报错 所以要写一个无参的构造函数
this
this关键字的作用:
Ø解决局部成员变量名重名问题给成员赋值
Ø代指当前对象
Ø在构造方法中可以调用本类中其他构造
Ø调用当前对象中其他成员方法
以上四种作用中解决局部成员变量名重名问题给成员赋值的作用最为常用将会注重介绍
其余四种作用可以仅作了解
解决局部成员变量名重名问题给成员赋值
错误示范
public WifeConcubine(String name) {
name=name;
System.out.println(name+"对象已被创建");
}
由于变量引用的就近原则 name=name本质上是自己对自己赋值 对象的name属性并没有被调用赋值
此时便需要this关键字来强制引用对象的属性
示范
public WifeConcubine(String name) {
this.name=name;
System.out.println(name+"对象已被创建");
}
加入this关键字便可以将构造参数的值赋给对象的属性
此时控制台输出
代指当前对象
当对象创建完成后 在构造方法或普通方法中this可以引用到对象的内存地址
比如
在WifeConcubine类中修改构造方法和flowerViewing方法中加入
System.out.println(this);
在主函数中加入打印xiuying对象的内存地址语句
System.out.println(xiuying);
public WifeConcubine(String name) {
System.out.println(this);//代指当前对象
this.name=name;
System.out.println(name+"对象已被创建");
}
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
System.out.println(this);
}
//创建一个嫔妃对象 李秀影
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine("李秀影");//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
System.out.println(xiuying);//用于验证
控制台输出
通过控制台的输出可知
this可以代指当前创建的对象
在构造方法中可以调用本类中其他构造
在构造方法中直接使用this可以条用其他构造方法
示例
在无参的构造方法中调用有参构造
public WifeConcubine() {
this("王梦竹");//调用有参构造方法
System.out.println("对象已被创建");
}
Simulation中创建一个嫔妃对象 王梦竹 同时会调用无参的构造方法 构造方法中this同时会调用有参构造对其进行赋值
//创建一个嫔妃对象 王梦竹 同时会调用无参的构造方法 构造方法中this同时会调用有参构造对其进行赋值
WifeConcubine mengzhu= new WifeConcubine();
控制台输出
注意事项
this() 在调用构造方法时候必须在第一行
调用当前对象中其他成员方法
this可以调用当前对象的其它方法
示例:
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
this.eat();
}
此时吃饭方法的输出语句将会输出两遍
控制台输出
封装
是什么?
封装:将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
为什么?
在没有使用封装之前,我们对数据的操作都是进行直接赋值。这样使用可能会产生一些不合理的赋值,不合理的操作。保证数据安全性。
封装的好处
➠只能通过规定的方法访问数据
➠隐藏类的实现细节
➠方便加入控制语句
➠方便修改实现
权限
在实现封装之前需要先了解权限修饰符
权限修饰符的作用如下
public(公共的)
具有最大的访问权限,可以同项目访问任何一个包下的类、接口、异常等。
protected(受保护)
它的主要作用就是保护(继承)子类的。它的含义在于子类可以使用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西。
默认的
即不写权限修饰符。它是针对本包(同包)访问而设计的,任何处于本包下的类、接口、异常等,都可以互相访问。
private(私有的)
它的访问权限仅限于类的内部,主要用于封装。
访问权限可以参考下表
get、set
那么到底如何建立一个封装类呢?
第一步、修改对象属性的访问权限为private(私有的)
private String name;//姓名
private int age;//年龄
private double height; //身高
private String weight;//体重
private String position;//位份
private int appearance;//颜值
private String hobby;//爱好
private String familyBackground;//家庭背景
private String address;//住址
第二步、加入setter/getter方法
set方法用于读取对象属性数据
get方法用于写入对象属性数据
示例:对对象姓名进行封装
public void steName(String name) {
this.name=name;
}
public String getNmae() {
return name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
通常年龄设置为不小于0岁和不大于160岁 我们可以在代码中对此进行限制 来保证数据的合理性
public void setAge(int age) {
if(age>=0&&age<=160) {
this.age = age;
}
}
问什么写成这个样子?
走的人多了便也成了路 写的人多了就成了规范
通常我们不会手写setter/getter方法,一般Java编辑器会有快捷方式生成setter/getter方法 可以自行百度
以eclipse为例
Alt+Shift+S
WifeConcubine类的完整代码
package com.qingsu.personnel;
public class WifeConcubine {
//我们将对象的特征称之为对象的属性 将对象的行为称之为对象的方法
//后宫佳丽的共有属性 (对象的属性)
private String name;//姓名
private int age;//年龄
private double height; //身高
private String weight;//体重
private String position;//位份
private int appearance;//颜值
private String hobby;//爱好
private String familyBackground;//家庭背景
private String address;//住址
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 double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getAppearance() {
return appearance;
}
public void setAppearance(int appearance) {
this.appearance = appearance;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getFamilyBackground() {
return familyBackground;
}
public void setFamilyBackground(String familyBackground) {
this.familyBackground = familyBackground;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//后宫佳丽的共有方法 (对象的方法)
public WifeConcubine() {
System.out.println("对象已被创建");
}
public WifeConcubine(String name) {
this.name=name;
System.out.println(name+"对象已被创建");
}
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
}
//和皇上一起吃饭
public void eat() {
System.out.println("这是一个吃饭的行为");
}
//跳舞
public void dancing() {
System.out.println("这是一个跳舞的方法");
}
//侍寝
public void waiter() {
System.out.println("这是一个侍寝的方法");
}
}
修改Simulation类中对属性赋值方法的写入和读取
package com.qingsu.personnel;
public class Simulation {
public static void main(String[] args) {
//创建一个嫔妃对象 王梦竹 同时会调用无参的构造方法 构造方法中this同时会调用有参构造对其进行赋值
WifeConcubine mengzhu= new WifeConcubine();
//创建一个嫔妃对象 李秀影
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine("李秀影");//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
//对李秀影对象内的属性进行赋值
xiuying.setAge(19); //对年龄进行赋值
xiuying.setHeight(1.66);//对身高进行赋值、
xiuying.setPosition("美人");//对位份进行赋值
xiuying.setAppearance (7); //颜值
xiuying.setHobby("弹琴"); //爱好
xiuying.setFamilyBackground("父亲为户部侍郎"); //家世
xiuying.setAddress("钟粹宫");
System.out.println(xiuying.getName()+"是后宫模拟器的一员,年龄"+xiuying.getAge()+"岁,身高"+xiuying.getHeight()+",在后宫的位份是"+xiuying.getPosition()+",她的爱好是"+xiuying.getHobby()+",她的家世"+xiuying.getFamilyBackground()+",她的住址是"+xiuying.getAddress());
//继续创建一个嫔妃对象 季凌菲
WifeConcubine lingfei = new WifeConcubine("季凌菲");//直接对姓名进行赋初值
lingfei.setAge(21); //对年龄进行赋值
lingfei.setHeight(1.71);//对身高进行赋值
lingfei.setPosition("妃");//对位份进行赋值
lingfei.setAppearance (7); //颜值
lingfei.setHobby("舞蹈");
lingfei.setFamilyBackground("父亲为兵部尚书");
lingfei.setAddress("景仁宫");
System.out.println(lingfei.getName()+"是后宫模拟器的一员,年龄"+lingfei.getAge()+"岁,身高"+lingfei.getHeight()+",在后宫的位份是"+lingfei.getPosition()+",她的爱好是"+lingfei.getHobby()+",她的家世"+lingfei.getFamilyBackground()+",她的住址是"+lingfei.getAddress());
//调用对象的方法 实现对象的行为
xiuying.dancing();
xiuying.eat();
xiuying.waiter();
xiuying.flowerViewing();
}
}
static(静态变量,方法)
是什么?
静态的变量与方法不属于对象,而是属于类 我们常说的类变量、类方法(静态变量、静态方法)就是指的是static修饰的变量
区别
◆静态方法属于类本身,非静态方法属于从该类生成的每个对象。
◆如果您的方法执行的操作不依赖于其类的各个变量和方法,请将其设置为静态(这将使程序的占用空间更小)。否则,它应该是非静态的。
◆在外部调用静态方法时,可以使用”类名.方法名”的方式,也可以使用”对象名.方法名”的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
◆静态方法在访问本类的成员时,只允许访问静态成员〈(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法:实例方法则无此限制
扩展:
◆成员变量有两种:实例变量和类变量(也,称静态变量,静态域)。
◆成员方法有三种:实例方法,类方法(也称静态方法),构造方法(无返回值,方法名和类名一致)。
实列变量和静态变量的区别:
我们一般将成员变量下分为实例变量和静态变量 他们之间的区别如下
1、生命周期的不同:
实例变量随着对象的创建而存在随着对象的回收而释放。静态变量随着类的加载而存在随着类的消失而消失。
2、调用方式不同:
实例变量只能被对象调用。
静态变量可以被对象调用,也可以用类名调用。(推荐用类名调用)
3、数据存储位置不同:
实例变量数据存储在堆内存的对象中,所以也叫对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。
静态使用时需要注意的事项:
1、静态方法只能访问静态成员。(非静态既可以访问静态,又可以访问非静态)
2、静态方法中不可以使用this或者super关键字。
3、主函数是静态的
4、我们一般将静态变量单独从成员变量中拿出来作为一个单独的变量来用。通俗的讲就是有三种变量
成员(实例)、局部、静态(类)。在正常的面试过程中我们都可以这样理解。但是在大学的考试中标准答案要求你将静态变量归为成员变量。
静态导包
静态导包就是java包的静态导入,用import static代替import静态导入包是JDK1.5中的新特性。
静态导包后可以直接调用方法 不需要使用类名
代码块
Java中有静态代码块、构造代码块、构造函数、普通代码块。
{}这就是代码块
静态代码块
这些代码块的执行顺序有一定的规范 有些面试会问!
static修饰的代码块便是静态代码块 位于类的内部 方法外部
示例
static {
System.out.println("这是静态代码块");
}
执行:静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。可以理解为最先被执行。
作用:
一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如项目启动时需要加载的缓存资源,我们就可以都放入静态代码块中。
构造代码块
不用static修饰的便是构造代码块 位于类的内部 方法外部
执行:创建对象时执行 可以理解在构造方法执行时加入的代码
作用:有多个构造方法时使用
构造函数代码块
构造函数中的代码
普通代码块
方法中的代码
执行顺序
静态代码块>构造代码块>构造函数>普通代码块
示例
public class StaticDemo {
//静态变量 类变量
static public String name;
static int a=0; //静态变量 只能由静态方法访问
//实例变量
public int age;
//静态代码块
static {
System.out.println("这是静态代码块");
}
//构造代码块
{
System.out.println("这是构造代码块");
}
public StaticDemo(){
System.out.println("这是构造方法代码块");
}
//静态方法
public static void eat() {
System.out.println("这是一个吃饭的静态方法可以使用类名访问、普通代码块");
a++;
System.out.println(a);
}
}
使用
package com.qingsu.personnel;
import static java.lang.System.out;//静态导包
public class StaticDemoTest {
public static void main(String[] args) {
//对静态变量赋值直接使用类名访问 此时静态代码块优先执行
StaticDemo.name="张三";
out.println(StaticDemo.name);
//实例化一个对象 此时构造代码块优先执行
StaticDemo testDemo = new StaticDemo();
//使用类名对对象赋值
testDemo.name="王五";
//静态变量归类所以 此时应该打印王五 所以不推荐使用对象名进行访问
out.println(StaticDemo.name);
testDemo.age=18;
//这是一个静态方法 既可以使用类名访问 也可以使用对象名访问
testDemo.eat();
StaticDemo.eat();
}
}
控制台输出
继承
在干货开始前写一些无关的牢骚。在Java中一切被人们熟知且常用的技术、特性、技巧都是为了解决需求而存在的!在学习Java的时候一定要理解其解决了什么需求,而不是对其进行简单的记忆。
同理作为Java三大特性(封装、继承、多态)之一的继承自然也是为了解决需求而诞生的。
那么继承解决了什么需求呢?
我们的需求是制作一个后宫模拟器。在此之前我们已经创建了一个后宫群妃的对象。但是后宫并不仅仅只有群妃、还有宦官、侍卫、医生、厨师、宫女等等。当我们创建这个对象的时候发现他们会有一些共有的属性和方法,比如身高、年龄、性别、体重、吃饭、走动等等。继承的存在就是为了解决这些共有属性在创建对象时多次书写代码重复和修改问题。
因此我们可以创建一个父类设置共有的属性、方法用于被需要的类进行继承。
如何使用?
第一步 找出共有的属性、方法创建父类(对象)
以后宫模拟器为例 创建一个人的父类 此时需要使用
package com.qingsu.personnel;
public class Person {
protected String name;//姓名
protected int age;//年龄
protected boolean sex;//true 代表男 false 代表女
protected String weight;//体重
//自动生成的get/set
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 boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
//共有的方法
protected void eat() {
System.out.println("吃的方法!");
}
protected void sleep() {
System.out.println("这是一个睡觉的方法");
}
}
第二步 编写继承于父类的子类 群妃、宦官、医生
继承关键字为extends 具体句法为 子类 extends 父类
示例
修改WifeConcubine类 删除继承的属性及方法
package com.qingsu.personnel;
public class WifeConcubine extends Person{
//我们将对象的特征称之为对象的属性 将对象的行为称之为对象的方法
//后宫佳丽的共有属性 (对象的属性)
private String position;//位份
private int appearance;//颜值
private String hobby;//爱好
private String familyBackground;//家庭背景
private String address;//住址
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public int getAppearance() {
return appearance;
}
public void setAppearance(int appearance) {
this.appearance = appearance;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
public String getFamilyBackground() {
return familyBackground;
}
public void setFamilyBackground(String familyBackground) {
this.familyBackground = familyBackground;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
//构造方法
public WifeConcubine() {
}
public WifeConcubine(String name) {
this.name=name;
System.out.println(name+"对象已被创建");
}
//后宫佳丽的共有方法 (对象的方法)
//赏花
public void flowerViewing() {
System.out.println("这是一个赏花的行为");
}
//跳舞
public void dancing() {
System.out.println("这是一个跳舞的方法");
}
//侍寝
public void waiter() {
System.out.println("这是一个侍寝的方法");
}
}
宦官 Eunuch 类
package com.qingsu.personnel;
public class Eunuch extends Person{
//属性
private String department;//部门 十二监、四司、八局 司礼监、内官监、司设监、御马监、神宫监、尚膳监、尚宝监、印绶监、直殿监、尚衣监、都知监、
//惜薪司、钟鼓司、宝钞司、兵仗局、银作局、浣衣局、巾帽局、针工局、内织染局、酒醋面局、司苑局
private String position;//级别
//get、set
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}//职级
//方法
//采购方法
public void purchase() {
System.out.println(name+"正在进行采购!");
}
//养马方法
public void raiseHorses() {
System.out.println(name+"正在养马!");
}
}
医生 Doctors类
package com.qingsu.personnel;
public class Doctors extends Person{
private String department;//院使、左、右院、御医、吏目、医士、食粮、切造医生
private String specialty;//特长
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getSpecialty() {
return specialty;
}
public void setSpecialty(String specialty) {
this.specialty = specialty;
}
//方法
//治病
public void cure() {
System.out.println(name+"正在治病");
}
//煎药
public void decoction() {
System.out.println(name+"正在煎药");
}
}
第三步 创建子类对象时可以直接使用父类的属性及方法
此时 姓名 吃饭等属性和方法在子类中并没有书写却可以直接使用
在Simulation类中新建对象及对对象赋值
package com.qingsu.personnel;
public class Simulation {
public static void main(String[] args) {
//创建一个嫔妃对象 王梦竹 同时会调用无参的构造方法 构造方法中this同时会调用有参构造对其进行赋值
WifeConcubine mengzhu= new WifeConcubine();
//创建一个嫔妃对象 李秀影
WifeConcubine xiuying; //创建一个数据类型为 WifeConcubine 我们自己定义的一个类 可以当作数据类型来用
xiuying= new WifeConcubine("李秀影");//将李秀影对象进行实例化 取得对象所需要的属性及方法 可以理解为对对象进行赋值
//对群妃 李秀影对象内的属性进行赋值
xiuying.setAge(19); //对年龄进行赋值
xiuying.setHeight(1.66);//对身高进行赋值、
xiuying.setPosition("美人");//对位份进行赋值
xiuying.setAppearance (7); //颜值
xiuying.setHobby("弹琴"); //爱好
xiuying.setFamilyBackground("父亲为户部侍郎"); //家世
xiuying.setAddress("钟粹宫");
System.out.println(xiuying.getName()+"是后宫模拟器的一员,年龄"+xiuying.getAge()+"岁,身高"+xiuying.getHeight()+",在后宫的位份是"+xiuying.getPosition()+",她的爱好是"+xiuying.getHobby()+",她的家世"+xiuying.getFamilyBackground()+",她的住址是"+xiuying.getAddress());
//继续创建一个嫔妃对象 季凌菲
WifeConcubine lingfei = new WifeConcubine("季凌菲");//直接对姓名进行赋初值
lingfei.setAge(21); //对年龄进行赋值
lingfei.setHeight(1.71);//对身高进行赋值
lingfei.setPosition("妃");//对位份进行赋值
lingfei.setAppearance (7); //颜值
lingfei.setHobby("舞蹈");
lingfei.setFamilyBackground("父亲为兵部尚书");
lingfei.setAddress("景仁宫");
System.out.println(lingfei.getName()+"是后宫模拟器的一员,年龄"+lingfei.getAge()+"岁,身高"+lingfei.getHeight()+",在后宫的位份是"+lingfei.getPosition()+",她的爱好是"+lingfei.getHobby()+",她的家世"+lingfei.getFamilyBackground()+",她的住址是"+lingfei.getAddress());
//调用对象的方法 实现对象的行为
xiuying.dancing();
xiuying.eat();
xiuying.waiter();
xiuying.flowerViewing();
//创建一个医生张晖对象
Doctors zhanghui = new Doctors();
zhanghui.setName("张辉");
zhanghui.setSex(true);//性别男
zhanghui.setDepartment("院使");//部门
zhanghui.setSpecialty("跌打伤");//特长
zhanghui.cure();//调用治病方法
zhanghui.decoction();//调用煎药方法
//创建一个宦官李黑对象
Eunuch lihei = new Eunuch();
lihei.setName("李黑");
lihei.setDepartment("御马监");
lihei.purchase();
lihei.raiseHorses();
}
}
控制台输出
Object类
Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法。
当我们不选择继承的类时 会自动继承Object类
整理和注意点
☀ 子类继承父类就拥有了父类的成员属性和方法
☀ 继承单继承 一个子类只能拥有一个父类 但是一个父类可以有多个子类
☀ 继承可以连续继承 子类也可以继续作为另一个类的父类
☀ 父类的构造方法不会被子类继承
重写
当我们继承父类的方法不能满足现有需求时,我们可以重写继承的属性和方法
重写需要满足以下原则
☀ 子类方法必须和父类方法具有相同的方法名称、参数列表和返回值类型
☀ 子类方法的访问权限只能比父类的更大或相同
☀ 子类方法抛出的异常不能大于父类被重写方法的异常
示例:
我们在宦官类中重写性别的isSex()方法使其只能为男性
public boolean isSex() {
return true;
}
当我们错误赋值为女性时我们也可以确保调用时为男性
//创建一个宦官李黑对象
Eunuch lihei = new Eunuch();
lihei.setName("李黑");
lihei.setDepartment("御马监");
lihei.setSex(false);
lihei.purchase();
lihei.raiseHorses();
System.out.println(lihei.isSex());
控制台输出
super
super关键字和this关键字有很多相通的地方 this用于调用本类的属性方法 super则是用于调用父类的属性和方法
因此super关键字只能出现在子类中
目的一般是用于特殊性指向 比如在重写方法时
具体使用方式如下
访问父类构造方法
super();
super(name);
注意: 只能放在第一行
•访问父类属性
super.name
•访问父类方法
super.print();
注意:就算在子类重写了父类的方法 super访问的依然是父类的原方法
示例 重写吃饭的方法
父类中原方法
//共有的方法
public void eat() {
System.out.println(name+"正在吃饭!");
}
子类
//吃饭方法
@Override
public void eat() {
// TODO 自动生成的方法存根
System.out.println("重写了吃饭的方法并且调用父类吃饭方法中的代码");
super.eat();
}
主方法
//创建一个宦官李黑对象
Eunuch lihei = new Eunuch();
lihei.setName("李黑");
lihei.setDepartment("御马监");
lihei.setSex(false);
lihei.purchase();
lihei.raiseHorses();
lihei.eat();
System.out.println(lihei.isSex());
控制台输出
在继承中对象的创建过程
☀JVM会先加载父类,再加载子类
☀在栈内存中为定义的变量分配内存空间,同时在堆内存中为真正的对象分配内存空间
☀调用构造器来为创建出的对象进行初始化:
•子类中所有的构造方法,默认都会先调用父类中无参的构造方法
•Object类的构造器是最先被执行的
重点
如果在父类中我们书写了一个有参的构造函数 那么默认的无参构造就不给了
因此在执行时就会出现没有父类无参构造的错误 因为在子类的构造函数中默认含有super()无论写不写
因此解决方法一般有两种
在父类里书写一个无参构造
在子类中的无参构造函数中主动调用有参构造即:super(x);
重点
我们默认父类里必须有无参构造 这是规范 你好我好大家好事情干嘛不做呢
final
作用
为了提高程序的安全性,Java中提供了fianl修饰符,来修饰类、变量、方法等。
用法
•final修饰类:表示该类不能被继承
•final修饰变量:表示该变量只能被赋值一次 必须手工为变量赋值一次(包括成员变量,如果赋默认值,没有任何意义)
•final修饰方法:表示该方法不能被覆盖
多态
什么是多态或者为什么用多态呢?
以我们现在后宫模拟器的需求而言 我们缺少灵魂 那就是皇帝 后宫的一切都是为皇族而服务的
那么我现在有一个需求 那就是要召见后宫的妃子 太医 太监
如果不使用多态我们需要使用对象.方法的方式进行需求的实现 没有一个统一的调用方式会导致后期的维护相当复杂
我们先简单的实现这个需求对多态进行一个初步的了解
多态的形式一般分为两种形式 :父类类型当做方法参数类型 父类类型当做返回值类型
下面示例使用父类类型当做方法参数类型实现多态
//在Person父类中加入召见方法
public void Summon() {
System.out.println("这是一个被召见的方法");
}
//在妃子子类中重写这个方法
//召见
public void Summon() {
System.out.println(position+":"+name+"正在被皇帝召见!");
}
//在医生子类中重写这个方法
//召见 多态讲解
public void Summon() {
System.out.println("太医"+name+"正在被皇帝召见!");
}
//在宦官子类中重写这个方法
public void Summon() {
System.out.println("太监"+name+"正在被皇帝召见!");
}
//编写皇帝类实现召见需求
public class Emperor {
//需要传入父类参数 向上转型的方式 进行召见函数操作
public void ESummon(Person person) {
person.Summon();
}
}
//在测试类中进行测试 传入不同参数可以实现不同的人
//多态测试 妃子 医生 宦官对象在上面皆以创建
//WifeConcubine lingfei = new WifeConcubine("季凌菲");
// Doctors zhanghui = new Doctors();
// Eunuch lihei = new Eunuch();
Emperor emperor = new Emperor();
//同一个方法实现传入不同的参数实现不同的需求
emperor.ESummon(lingfei);
emperor.ESummon(zhanghui);
emperor.ESummon(lihei);
控制台输出
以上便是多态的一个简单的小例子具体的实现方式看以下详细讲述
向上转型
**目的:**将子类对象转换成父类类型 使父类可以调用子父类中共有的函数
形式如下:
父类 变量名 = new 子类对象;
父类类型可以接收子类对象
//上面的示例便是使用了向上转型
//医生对象转换成了父类类型 父类可以调用子类中被重写的函数
Doctors zhanghui = new Doctors();
Person person = zhanghui;
person.Summon();
注重点:
☀ 向上转型 父类中与子类成员变量名相同 输出成员变量 调用的是父类的成员变量
☀ 向上转型 调用父类子类共有的属性和方法 不能调用特有的
☀ 当子类进行方法的重写后,调用的是子类的重写后的方法
instanceof
作用:对对象进行比较 同一对象类型返回true 不同返回false
实例
Doctors zhanghui = new Doctors();
Person person = zhanghui;
person instanceof Eunuch;//将返回false
person instanceof Doctors;//将返回true
向下转型
目的: 实现对子类对象特定方法的调用
使用过程
先进行向上转型
Doctors zhanghui = new Doctors();
Person person = zhanghui;
然后进行向下转型
//向上转型
Doctors zhanghui = new Doctors();
Person person = zhanghui;
//向下转型
Doctors doctors = (Doctors)person;
doctors.cure();//调用医生的特定方法
整个过程实现的是使用Person父类可以接受所有子类的对象 通过对子类对象的判断来确定子类对象的类型 进行强制转换 接收子类对象的信息 然后调用子类对象的特有方法完成相关需求
因此向下转型前提必须要先向上转型 才能实现
使用示例:
需求:后宫既然是为皇帝服务的 那么每个人都有特定的服务方向 比如太医治病 太监养马 妃子侍寝等等 我们可以使用多态和向下转型来完成整个需求
妃子侍寝
//侍寝
public void waiter() {
System.out.println(name+"正在给皇帝侍寝");
}
太监养马
//养马方法
public void raiseHorses() {
System.out.println(name+"正在给皇上养马!");
}
太医治病
public void cure() {
System.out.println(name+"正在给皇上治病");
}
服务方法
//向下转型 实现对子类特有方法的调用
//服务方法
public void service(Person person) {
if(person instanceof Eunuch) {
Eunuch eunuch = (Eunuch) person;
eunuch.raiseHorses();
}else if(person instanceof WifeConcubine) {
WifeConcubine wifeConcubine = (WifeConcubine) person;
wifeConcubine.waiter();
}else if(person instanceof Doctors) {
Doctors doctors = (Doctors)person;
doctors.cure();
}else {
}
测试类
//多态测试 妃子 医生 宦官对象在上面皆以创建
//WifeConcubine lingfei = new WifeConcubine("季凌菲");
// Doctors zhanghui = new Doctors();
// Eunuch lihei = new Eunuch();
Emperor emperor = new Emperor();
System.out.println("向上转型");
emperor.ESummon(lingfei);
emperor.ESummon(zhanghui);
emperor.ESummon(lihei);
System.out.println("向下转型");
emperor.service(lingfei);
emperor.service(zhanghui);
emperor.service(lihei);
控制台输出
多态的单例应用
需求
主人有猫、狗、乌龟三种宠物
主人需要给三种宠物喂食 猫吃鱼 狗吃骨头 乌龟吃龟粮
主人和每种宠物有一种特殊的互动 猫和主人玩红外线 狗和主人玩小球 乌龟和主人玩水花
//编写宠物父类
package com.qingsu.polymorphismTwo;
public class Pet {
protected String name;//宠物姓名
public Pet() {
}
public void eat(){
System.out.println("这是一个公有的吃饭方法");
}
}
编写猫咪子类
package com.qingsu.polymorphismTwo;
public class Cat extends Pet {
//构造函数 初始化宠物姓名
public Cat(String name) {
this.name=name;
}
public void eat() {
System.out.println("主人正在给猫咪"+name+"喂鱼");
}
public void palyRedX() {
System.out.println(name+"猫咪正在和主人玩红外线");
}
}
编写狗狗子类
package com.qingsu.polymorphismTwo;
public class Dog extends Pet {
public Dog (String name) {
this.name=name;
}
public void eat() {
System.out.println("主人正在给"+name+"狗狗喂骨头");
}
public void playball() {
System.out.println(name+"狗狗正在和主人玩球");
}
}
编写乌龟子类
package com.qingsu.polymorphismTwo;
public class Tortoise extends Pet{
public Tortoise(String name) {
this.name=name;
}
public void eat() {
System.out.println("主人正在给"+name+"乌龟喂龟粮");
}
public void palySplash() {
System.out.println(name+"乌龟正在配主人玩水花");
}
}
编写主人类
package com.qingsu.polymorphismTwo;
public class Master {
//多态 向上转型 调用共有方法
//父类类型当做方法参数类型
public void feed(Pet pet) {
pet.eat();
}
//父类类型当做返回值类型
public Pet getPet(int num,String name) {
if(num==1) {
return new Dog(name);
}else if(num==2) {
return new Cat(name);
}else if(num==3) {
return new Tortoise(name);
}else {
return null;
}
}
//多态 向下转型 调用特有方法
public void paly(Pet pet) {
if(pet instanceof Dog) {
Dog dog = (Dog) pet;
dog.playball();
}else if(pet instanceof Cat) {
Cat cat = (Cat) pet;
cat.palyRedX();
}else if (pet instanceof Tortoise) {
Tortoise tortoise = (Tortoise) pet;
tortoise.palySplash();
}
}
}
编写测试类
package com.qingsu.polymorphismTwo;
public class Test {
public static void main(String[] args) {
//实例化对象
Cat xiaobai = new Cat("小白");
Dog xiaohei = new Dog("小黑");
Tortoise xiaolv = new Tortoise("小绿");
Master master = new Master();
//调用喂食方法
//父类类型当做方法参数类型
master.feed(xiaobai);
master.feed(xiaohei);
master.feed(xiaolv);
//父类类型当做返回值类型 省去实例化对象
Pet pet= master.getPet(1,"小白");
pet.eat();
pet= master.getPet(2,"小黑");
pet.eat();
pet = master.getPet(3,"小绿");
pet.eat();
//调用玩耍方法
master.paly(xiaobai);
master.paly(xiaohei);
master.paly(xiaolv);
}
}
控制台输出