面向对象(Object Oriented)
上一篇我总结了面向对象和面向过程的一些基础知识以及简单地运用面向对象创建一个简单事物,那么这一篇就接着上一篇的内容,继续总结面向对象中应该注意的一些事项以及如何封装。
class Test01{
public static void main(String[] args){
Point p1=new point();
Point p2=new point();
p1.x=10;
p1.y=20;
p2.x=30;
p2.y=40;
System.out.println(p1.getDistence(p2));
}
}
class Point{
double x;
double y;
public double getDistence(Point other){
return Math.hypot(x-otherx,y-other.y);
}
}
这是上一篇的代码,我们从第一张图可以发现两个问题:
- 我们可以通过对象直接修改对象中的成员变量。弊端:如果赋予了一个错误的值,那么肯定会导致程序不能正常运行。通俗来讲,有些东西可以被看到,有些不能被看到,就好比你的内裤,或者你的银行卡密码等一些隐私东西,也就是说我们的成员变量并不是完全需要向外界可访问,因为如果能被外界访问,那么肯定也能被外界修改。为了防止外界直接修改对象的成员变量,这里我们就引用了一个关键字——private。
- 我们只能将对象创建完毕之后,在进行对成员变量的赋值。
private关键字
其实它就是一个权限关键字,它和 public 、protected 、默认不写 这三个是相似的。
private 表示私有权限,意思就是该成员或成员变量只能在类中被访问,我们引入私有权限是为了防止外界访问成员变量,但是自己可以访问,所以这个时候就需要判断访问的来源,这时我们又引入了两个新的东西—— setter 和 getter,两个都是Java中规范的,setter 是修改器,getter 是访问器,主要负责修改成员变量和访问成员变量(返回成员变量),两者本身就是成员函数,命名方式一般为 setXxx 和 getXxx,如下图所示:
class Test02{
public static void main(String[] args){
Person p=new Person();
p.setAge(10);
System.out.println(p.getAge());
}
}
class Person{
private int age;
void show(){
System.out.println("今年"+age+"岁");
}
public void setAge(int a){
if(a<0||a>150){
age=0;
}else{
age=0;
}
}
public int getAge(){
return age;
}
}
封装
说到封装就不得不说面向对象的三大特征:封装、继承、多态。
封装从字面意义上来看,就是将繁杂的东西进行统一管理。不如为了方便拿笔,我们就会准备一个文具盒;为了方便放衣服,我们就会准备一个衣柜等等。封装最大的好处就是节约代码,方便管理,降低耦合性。
那封装在代码中怎么体现呢?
- 循环:主要解决有规律且重复性执行的代码
- 函数:主要解决具有独立功能的一段代码,后期进行复用即可
- 数组:主要解决多种同类型数据的操作,统一操作,方便类管理
- 类:主要将数据和行为进行统一方便操作,仿真现实中的事物
我们会发现在上面的代码中变量非常多,这样很容易搞混,其实不多,在这里变量分为两种局部变量和成员变量,那两者有什么区别呢?如何区分他们之间的区别呢?
- 存储位置:局部变量存储在栈内存中函数所属的所属空间里;成员变量存储在堆内存中对象的所属空间里
- 生命周期:局部变量随着函数的进栈而存在,随着函数的弹栈而消失;成员变量随着对象的创建而存在,随着对象的销毁而消失。
- 访问范围:局部变量的访问范围仅仅在函数当中
- 初始化值:局部变量不许先进行初始化,之后才能被调用;成员变量在创建时就有默认初始化
成员变量赋值(问题2)
有些对象创建之前成员变量就有值了,有些对象创建之后成员变量才有值(不包括默认初始化),那么我们如何在创建对象之前或之中对成员变量进行赋值操作呢?这就要引入新的知识点:构造函数!
构造函数的主要作用就是在创建对象的同时对成员变量进行赋值
构造函数的格式:
权限修饰符 函数名(参数列表){
函数体;
}
对比和之前的函数,构造函数没有函数类型,也没有返回值类型(但是这并不代表着没有 return ),函数名必须是类名!但凡创建一个对象,构造函数就执行一次。
class Test02{
public static void main(String[] args){
Person p1=new Person();
p1.setAge(10);
System.out.println(p.getAge());
Person p2=new Person(20);
Person p3=new Person("小白",30);
}
}
class Person{
private int age;
private String name;
public Person(){ //无名 无年龄
System.out.println("一个人诞生了。。。");
}
public Person(int b){ //无名 有年龄
age=b;
System.out.println("一个人诞生了。。。"+age);
}
public Person(String c,int a){ //有名 有年龄
name=c;
age=a;
System.out.println("一个人诞生了。。。"+age+name);
void show(){
System.out.println("今年"+age+"岁");
}
public void setAge(int a){
if(a<0||a>150){
age=0;
}else{
age=0;
}
}
public int getAge(){
return age;
}
public void setName(String c){
name=c;
}
public String getName(){
return name;
}
}
成员变量的赋值其实经历了三个阶段:默认初始化——显式初始化(类中成员变量被赋值)——针对性初始化(构造函数)
对象的内存图解Ⅱ
- 在堆内存中开辟一个空间并分配一个地址
- 对成员变量进行默认初始化
- 相应的构造函数进栈,刚开始就对成员变量进行显式初始化
- 接着再去执行构造函数中的内容针对性初始化
- 构造函数执行完毕,弹栈,将对象的地址赋值给相应变量即可