面向对象的三大特性,封装、继承、多态
什么是封装?什么是多态?什么又是继承?怎么去理解?本篇幅详细的说一下OOP的封装、构造方法、静态。
一、封装
简单的说就是 (数据的隐藏)
它可以提高程序的安全性,隐藏代码的实现细节,统一接口,提高系统的可维护性
首先封装的最大特点就是,属性私有!常使用get
和set
方法获取设置内部细节
封装的原则是:(该漏的漏,该藏的藏) 提高代码的复用性
(1) 隐藏事物的属性(实现细节) 提高安全性
(2) 隐藏事物的实现细节
(3) 提供一个公共的访问方式 get,set
(1)private关键字
private (私有的私密的) 可以修饰成员变量
,成员方法
,构造方法
,内部类
被private修饰,只能在类的内部直接访问,在类的外部是不能直接访问的
注意: private只是封装的一种体现形式
public class Demo01 {
public static void main(String[] args) {
Student s = new Student();
// s.name = "陈毅";属性私有之后再student类之外就无法直接访问了
s.study();//通过公开的方法间接的访问私有的属性
// s.play();
// s.age = -18;
s.setAge(88);
System.out.println(s.getAge());
}
}
class Student {
//私有的属性
private String name;
//私有的内容只能在本类中被访问
private int age;
public void study() {
System.out.println("好好学习,天天加班" + name);
play();
}
private void play() {
System.out.println("悄悄的玩游戏,一般不告诉");
}
public void setAge(int i) {
if (i >= 0 && i <= 120) {
age = i;
}
}
public int getAge(){
return age;
}
}
(2)Get和Set
1、当属性被私有化之后,外界不能直接访问了,所以需要提供的公共的访问方式,让外界可以间接的访问成员变量 getset方法就是公共的访问方式
2、属性赋值的格式: private 数据类型 变量名;
public void set+首字母大写的变量名(数据类型 参数名){
变量名 = 参数名;
}
3、属性值获取的格式:
public 返回值类型 get+首字母大写的变量名(){
return 变量名;
}
举个栗子:
public class Demo02 {
public static void main(String[] args) {
OldMan o = new OldMan();
//此时不能直接访问属性值了,要通过OldMan类型中提供的公共的访问方式去访问
o.setName("张三"); //通过公共的访问方式可以间接的访问这些私有的属性
System.out.println(o.getName());//输出张三
}
}
class OldMan { //创建一个老男人类 成员变量有name 行为有获得name 设置name
//一般所有的属性都要私有,所以成员变量使用private修饰
private String name; //私有的只能在本类中被访问
//name提供公共的访问方式
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
再举个栗子:
//类
public class Student { //创建一个学生类,学生有姓名,学号,性别,年龄
//属性私有 private英文是 :私有的意思
private String name; //名字
private int id; //学号
//** private char sex; //性别
private int age; //年龄
//提供一些操作这个属性的方法!
//提供一些public的get,set方法
//学习()
//睡觉()
//get 获得这个数据
public String getName(){
return this.name;
}
//set 给这个数据设置值
public void setName(String name){
this.name=name;
}
//idea 使用alt+insert可以快捷键生成get和set方法
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
if (age>120 || age<0){
this.age=3;//小朋友3岁
}else {
this.age = age;
}
}
}
/*Student s1 = new Student();
s1.setName("暴书源");
System.out.println(s1.getName());
s1.setId(2020020199);
System.out.println(s1.getId());
s1.setAge(-70);//不合法
System.out.println(s1.getAge());
*/
练习(截图题目自己做,然后在参考以下答案)
1、定义一个汽车类:
(1)属性 颜色color、 轮胎个数number 、 价格price
(2)行为 跑 查看汽车详情(showInfo) 要求查看汽车详情的方法可以输出颜色、轮胎个数、价格
(3)属性私有化并提供公共的访问方式
public class Demo03 {
public static void main(String[] args) {
Car c = new Car();
c.setColor("绿色");
c.setNumber(3);
c.setPrice(1111);
System.out.println(c.getColor());
System.out.println(c.getNumber());
System.out.println(c.getPrice());
c.showInfo();
}
}
class Car {
private String color; //一个类中的属性,一般都要进行私有
private int number; //私有意味着不能在当前类以外进行直接访问
private int price;
//要提供公共的访问方式用于操作这些属性
//属性的修改值的方式 set
//通过该方法可以间接的给color属性赋值
public void setColor(String c){
color = c;
}
//通过间接的方式获取值
public String getColor(){
return color;
}
public int getNumber() {
return number;
}
public int getPrice() {
return price;
}
public void setNumber(int n){
number = n;
}
public void setPrice(int p){
price = p;
}
//属性获取值的方式 get
public void run() {
System.out.println("跑");
}
public void showInfo() {
System.out.println(color + number + price);
}
}
(3)变量的访问原则
(1) 变量的定义:定义变量的时候一定是带着数据类型的
(2) 变量的使用:如果不带数据类型的变量,就一定是在使用某个变量
(3) 就近原则:
① 访问变量的时候,在访问语句所在的大括号中寻找有没有定义此变量,如果找到了就用,如果没有找到,就会到括号外去寻找,如果找到了就用,找不到就报错
② 当成员变量和局部变量同名的时候,访问变量就看谁距离访问的这个语句更近,就使用谁的数据
(4) this关键字
this.变量名 访问的是成员变量,不加this的一般都是局部变量
作用: 可以区分同名的成员变量和局部变量
① 表示本类当前对象的引用
② 哪个对象调用this所在的方法,this就表示哪个对象
public class Demo04 {
public static void main(String[] args) {
Girl g = new Girl();
g.setName("小明");
System.out.println(g.getName());
g.show();
System.out.println(g);
//哪个对象调用this所在的方法,this就代表哪个对象
Girl g2 = new Girl();
g2.show();
}
}
//成员变量和局部变量同名
//成员变量和成员变量,不能出现同名 类变量不能同名
//局部变量和局部变量,不能出现同名 方法变量不能同名
//成员变量和局部变量同名,按照就近原则访问
class Girl{
//this可以理解为类中提供的一个成员,可以记录当前对象的地址值
private String name;//成员变量
public void setName(String name){//局部变量
//成员和局部同名,根据就近原则,
this.name = name;//给局部变量的name赋值
//加上this就一定是成员变量
}
public String getName(){
return name;
}
int a = 666; //成员变量
public void show(){
int a = 10;
System.out.println(a);//根据就近原则就是10
System.out.println(this);
//如果一个变量名前面加上了this,此时一定是一个成员变量
System.out.println(this.a);
}
}
二、构造方法
1、构造方法,有一些别称,构造器、构造函数、Constructor
2、作用:用于给对象的成员变量赋值,在new对象时,jvm虚拟机会自动调用 等对象创建完成之后,该对象的变量就已经有指定的值了
使用new关键字时,必须要有构造器(new的时候本质是在调用构造器)
3、格式:
修饰符 方法名 (参数列表){ 方法名 必须和类的名字相同
方法体;
}
-
修饰符:可以是public或者private,private修饰的就是私有的构造方法,public修饰的就是公开的构造方法
-
构造方法: 必须没有返回类型,也不能写void
-
构造方法可以有return,但是只能写return; 表示结束方法
4、注意事项:
(1) 构造方法是虚拟机自动调用
(2) 不能使用对象名调用构造方法
(3) 只要创建一个对象,构造方法就调用一次
public class Demo05 {
public static void main(String[] args) {
Person p = new Person();//() 表示调用了一个方法,括号内部放实际参数 (实际参数)
//此时创建对象的同时,对象就已经有了具体的值
Person p2 = new Person("小黑");
p2.show();
Person p3 = new Person("小白");
p3.show();
//set和构造方法的区别
//set方法主要用于创建对象完成之后,后期修改对象的属性值
//构造方法主要是创建对象的同时,给对象赋值属性值
}
}
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//如果一个类中没有书写任何一个构造方法,此时系统默认会提供一个空参构造
//最简单的构造方法,无参数构造方法
//格式 public 类名(形式参数){}
public Person(){
//只是用于创建对象,没有任何其他操作
System.out.println("空参构造被执行了");
}
//方法的重载:在同一个类中,方法名相同,参数列表不同,与返回值类型无关
public Person(String name){
this.name = name;
System.out.println("有参构造执行了");
}
public void show(){
System.out.println(name);
}
}
构造方法的注意事项:
1、构造方法可以是有参数的,也可以是没有参数的
(1) 如果没有参数,创建对象时调用构造方法,是不可以传递参数的,此时通过这种方式创建的对象,属性初始值均相同
(2) 如果有参数,外界可以通过构造方法,传递参数,给成员变量赋值,这样创建出来的对象,成员变量的值都是不同的
2、类中没有手动
添加任何构造方法,系统会默认提供一个空参构造
3、类中手动
添加了任何一个构造方法,系统都不会再提供
任何构造方法
4、构造方法也算重载:在同一个类中,方法名都是类名,参数列表一定不同,返回值类型直接没有
5、一般的类中,既要有空参构造,也要有全参构造,都是手动定义出来的
public class Demo06 {
public static void main(String[] args) {
Person p = new Person();//() 表示调用了一个方法,括号内部放实际参数 (实际参数)
//此时创建对象的同时,对象就已经有了具体的值
Person p2 = new Person("小黑");
p2.show();
Person p3 = new Person("小白");
p3.show();
//set和构造方法的区别
//set方法主要用于创建对象完成之后,后期修改对象的属性值
//构造方法主要是创建对象的同时,给对象赋值属性值
}
}
class Person{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//如果一个类中没有书写任何一个构造方法,此时系统默认会提供一个空参构造
//最简单的构造方法,无参数构造方法
//格式 public 类名(形式参数){}
public Person(){
//只是用于创建对象,没有任何其他操作
System.out.println("空参构造被执行了");
}
//方法的重载:在同一个类中,方法名相同,参数列表不同,与返回值类型无关
public Person(String name){
this.name = name;
System.out.println("有参构造执行了");
}
public void show(){
System.out.println(name);
}
}
2.创建对象的内存理解
1、创建一个对象的过程中,成员变量最多经过三次初始化
(1) 默认初始化
(2) 显式初始化
(3) 构造方法初始化
2、执行晚的内容,会把执行早的内容覆盖掉(最新的生效)
3、三种初始化的顺序,从早到晚: 默认初始化 > 显式初始化 > 构造方法初始化
4、一个对象创建的完整流程:
(1) 把创建对象所在的类.class加载到方法区
(2) 在栈内存中给该类型的引用开辟一块空间,用来存储堆内存中的地址
(3) 在堆内存中开辟空间,给成员变量赋值
(4) 给成员变量默认初始化赋值
(5) 给成员变量显式初始化赋值
(6) 给成员变量构造方法初始化赋值
(7) 把堆内存中的对象地址,赋值给栈内存中的引用
public class Demo07 {
public static void main(String[] args) {
Boy b = new Boy("小明",22);
b.show();
}
}
class Boy{
private String name;//此时默认值 null 此时叫做默认初始化赋值
private int age = 20;//在声明的同时并赋值,被称为显式初始化
//构造方法初始化
public Boy(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println(name + age);
}
}
三、静态
没有静态的时候
某个类型的对象都有一个相同的属性值,那么这个属性就没必要定义在所有的对象中,如果定义在所有的对象中,每个对象此时都要存储一份,会浪费空间,不利于后期维护,一旦要修改该属性的值,就需要修改所有对象的属性值
有了静态之后
如果某个类型的某个属性值都一样,那么就没有必要给每个对象都设计该属性的值,而是把这个属性前面加上一个static修饰符,加上static之后,该变量就存储到方法区中,此时该属性就是静态的属性,在这个类型中只存储一份,但是这个变量的数据可以被所有的对象访问,这样节省空间,如果要修改,此时修改一处,其他地方也全部会跟着修改
public class Demo08 {
public static void main(String[] args) {
//加上静态的属性,被称为类变量 静态变量
//随着类的加载而加载
//可以直接通过类名访问
System.out.println(OldMan.sex);
OldMan.sex = "男人";
OldMan o = new OldMan();
o.show();
OldMan o1 = new OldMan();
//想改变一下 性别的描述
o1.show();
OldMan o2 = new OldMan();
o2.show();
}
}
class OldMan{
String name;
static String sex = "男";
public void show(){
System.out.println(name + sex);
}
}
1.静态属性
1、静态关键字:static,静态 静止的,静态的内容不会随着对象的变化而变化
2、静态的加载时机:随着类的加载而加载。加载时进入方法区,进入方法区之后会在静态区中开辟空间存储数据
3、静态变量出现的时机早于对象
4、静态变量能被当前类的所有对象共享,该类的每一个对象都可以访问
5、以后可以使用类名访问静态变量,虽然通过对象名可以,但是静态变量属于类,还是使用类名访问
6、格式: 类名.静态变量名;
2.静态方法
1、在定义方法的时候,在修饰符的位置加上static,这个方法就是一个静态方法
2、静态方法,可以通过类名去访问,也可以通过对象名去访问
3、只能通过类名去访问的内容,本质上都是属于类最好都通过类名去访问
4、静态方法使用注意事项:
(1) 静态方法中不能访问成员变量
-
静态方法,没有创建对象的时候就能调用,成员变量是在对象完成之后才可以使用,
-
假如静态的方法能访问成员变量,就相当于在创建对象之前,就使用对象内容(所以不能访问成员变量)
(2) 静态方法能不能访问成员方法,不能,原理同上
(3) 静态方法只能访问静态的内容,可以访问静态的属性,也可以访问静态的方法
(4) 静态方法能不能有this关键字,this表示本类当前对象,有了对象才有this,所以还是不行
5、总结:
(1) 静态不能访问非静态的内容,非静态内容可以访问静态的内容
public class Demo09 {
}
class Teacher{
String name;
int age;
static String department = "教研室";// department静态属性,可以在创建对象之前被访问,也可以通过对象名去访问
//成员方法
public void sayHi(){ //非静态既可以访问静态的内容,也可以访问非静态的内容
//非静态可以访问静态的内容
System.out.println("哈喽,大家好,我是老师" + name + age + department);
}
public static void teaching(){ //静态不能访问非静态,非静态可以访问静态
System.out.println("师者传道受业解惑也");
// System.out.println("师者传道受业解惑也" + name); //name是非静态的,静态中不能访问非静态
// sayHi(); //方法是非静态的,必须对象创建完成之后才可以调用
}
}
3.静态变量和非静态变量的区别
1、概念上所属不同:
(1) 非静态变量属于对象
(2) 静态变量属于类
2、内存空间位置不同:
(1) 非静态变量存储在堆内存中,和对象一个区域
(2) 静态变量属于类,和类.class文件在一个区域,都在方法区中,只不过静态变量在静态区
3、存储的时间不同,生命周期不同
(1) 非静态变量属于对象,所以生命周期和对象一致,随着对象的创建而创建,随着对象的消亡而消亡
(2) 静态变量属于类,所以生命周期和类一致,随着类的加载而加载,.class的销毁而销毁
4、访问方式的不同:
(1) 非静态变量,只能通过对象名访问
(2) 静态变量,既可以通过类名访问,也可以通过对象名来访问