Java笔记-面向对象-封装

  • 类:确定对象将会拥有的特征(属性)和行为(方法)。
    类的特点:类是具有相同属性和方法的一组对象的集合。

  • 对象: 对象是唯一的
    创建对象:类名 对象名 = new 类名()
    使用对象:对象.属性 / 对象.方法名()

  • 成员变量与局部变量
    成员变量:在类中定义,用来描述对象将要有什么。(作用域在整个类内部都是可见的)
    局部变量:在类的方法中定义,在方法中临时保存数据。(作用域仅限于定义它的方法)

定义一个Car类

public class Car {

    String name;  // 名字
    String color; // 颜色
    int wheel;    // 轮子数量
    
    public void  reMake(String name) {
        
        this.name = name;
    }

外界使用Car类创建对象

package 面向对象;

import 面向对象.Car;

public class Demo1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Car car = new Car();
        car.name = "奔驰";
        car.color = "白色";
        car.wheel = 6;
        
        car.reMake("BMW");
        System.out.println( car.name);
    }

}

  • Java对象的内存分析:
    Car 这个类共有三个成员变量
public class Car {
     String name; 
     String color;
     int wheel;   
}
/// 创建car对象
Car c = new Car();
c.name = "奔驰";
c.color = "白色";
c.wheel = 6;

当使用Car类的声明一个c变量时
在栈内存中开辟一块存储空间给c变量,堆内存中开辟存储空间给创建的这个对象及其成员变量,此时c变量记录这个对象的内存地址,会指向这个对象引用
注意: 对象一旦被创建, 对象的成员变量也会分配默认的初始值,
成员属性默认的初始值:
int 0(默认值)
float 0.0f(默认值)
double 0.0(默认值)
char ''(默认值)
String(所有引用数据类型) null(默认值)

  • 成员变量与局部变量的区别:

    定义的位置上区别:
    1. 成员变量是定义在方法之外,类之内的。
    2. 局部变量是定义在方法之内。

    作用上的区别:
    1. 成员变量的作用是用于描述一类事物的公共 属性的。
    2. 局部变量的作用就是提供一个变量给方法内部使用而已。

    生命周期区别:
    1. 随着对象 的创建而存在,随着对象的消失而消失。
    2. 局部变量在调用了对应的方法时执行到了创建该变量的语句时存在,局部变量一旦出了自己的作用域
    那么马上从内存中消失。

    初始值的区别:
    1. 成员变量是有默认的初始值。
    数据类型 默认的初始值
    int 0
    float 0.0f
    double 0.0
    boolean false
    char ' '
    String(引用数据类型) null

      2. 局部变量是没有默认的初始值的,必须要先初始化才能使用。
    
  • 匿名对象:没有引用类型变量指向的对象称作为匿名对象。

匿名对象要注意的事项:
1. 我们一般不会给匿名对象赋予属性值,因为永远无法获取到。
2. 两个匿名对象永远都不可能是同一个对象。

匿名对象好处:简化书写。

匿名对象的应用场景:
1. 如果一个对象需要调用一个方法一次的时候,而调用完这个方法之后,该对象就不再使用了,这时候可以使用
匿名对象。
2. 可以作为实参调用一个函数。

  • JAVA中set()和get()方法的理解及使用

set是设置的意思,而get是获取的意思,比如setAge()和getAge(),表示设置年龄和获取年龄
然后我们来了解一下JAVA面向对象编程中的封闭性和安全性,封闭性即对类中的域变量进行封闭操作,即用private来修饰他们,如此一来其他类则不能对该变量访问。这样我们就将这些变量封闭在了类内部,这样就提高了数据的安全性,当我们想要操作这些域变量怎么办呢?我们可以通过两种方法,第一中即通过public方式的构造器(或称构造函数),对象一实例化就对该变量赋值。第二种就是通过上面提到的set和get方法,这里我举一个特定的例子,我定义一个Person类,该类中有name、age这两个私有域变量,然后我定义setname()、getname()、setage()、getage()这四个方法,通过这四个方法来实现对name和age的操作。这样一来,我不用直接对Person类中的域变量操作,而是通过set和get方法间接地操作这些变量,这样就能提高域变量的安全性,同时又保证了域变量的封装型。
set和get方法的使用场景,一般来说set和get方法都是对私有域变量进行操作的,所以大多数都是使用在包含特定属性的类实体中。

public class UserAccount {

    private String userName;
    private String sex;
    
    public void setUserName(String name) {
        userName = name;
    }
    
    public String getUserName() {
        return userName;
    }
    public void setSex(String str) {
        
        if (str.equals("男") || str.equals("女")) {
            sex = str;
        } else {
            sex = "男"; 
        }
    }
    public String getSex() {
        return sex;
    }
}

Java封装总结:
概念:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法类实现对隐藏信息的操作和访问。
好处:只能同规定的方法访问数据,隐藏类的实现细节,方便修改和实现。
实现步骤
① 修改属性的可见性:设为private
② 创建getter/setter方法:用于属性的读写
③ 在getter/setter方法中加入属性控制语句:对属性值的合法性进行判断

封装的好处

隐藏实际细节,提供公共的访问方式
提高了代码的复用性
提高安全性
封装原则
将不需要对外提供的内容都隐藏起来
把属性隐藏,提供公共方法对其访问。

  • 构造函数:

构造函数的作用: 给对应的对象进行初始化。

构造函数的定义的格式:

修饰符  函数名(形式参数){
    函数体...
}

构造函数要注意的细节:
1. 构造函数 是没有返回值类型的。
2. 构造函数的函数名必须要与类名一致。
3. 构造函数并不是由我们手动调用的,而是在创建对应的对象时,jvm就会主动调用到对应的构造函数。
4. 如果一个类没有显式的写上一个构造方法时,那么java编译器会为该类添加一个无参的构造函数的。
5. 如果一个类已经显式的写上一个构造方法时,那么java编译器则 不会再为该类添加 一个无参 的构造方法。
6. 构造函数是 可以在一个类中以函数重载 的形式存在多个 的。

创建对象时,jvm虚拟机就会调用到对应的构造方法,构造方法从java编译器在编译的 时候给加上去的。

jdk提供了一个java开发工具(javap)给我们进行反编译的,在终端敲javap会有相关提示操作指令。
mac上javap的路径/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin
javap 反编译工具的使用格式:

    javap -c -l -private 类名

疑问: java编译器添加 的无参构造方法的权限修饰符是 什么?
与类的权限修饰是一致的。

构造函数与普通 函数的区别:
1. 返回值类型的区别:
1. 构造函数是没有返回值类型 的,
2. 普通函数是有返回值类型的,即使函数没有返回值,返回值类型也要写上void。
2. 函数名的区别:
1. 构造函数的函数名必须要与类名一致,
2. 普通函数的函数名只要符合标识符的命名规则即可。
3. 调用方式的区别:
1. 构造函数是 在创建对象的时候由jvm调用的。
2. 普通函数是由我们使用对象调用的,一个对象可以对象多次普通 的函数,
4. 作用上的区别:
1. 构造函数 的作用用于初始化一个对象。
2. 普通函数是用于描述一类事物的公共行为的。

以下Baby类中提供了两个构造函数:
//婴儿类
class Baby{
    
    int id; //身份证
 
    String  name;  //名字

    //构造函数
    public  Baby(int i , String n){
        id  = i;
        name = n;
        System.out.println("baby的属性初始化完毕!!");
    }

    //无参 的构造函数
    public Baby(){
        System.out.println("无参的构造函数被调用了..");
    }

    public void cry(){
        System.out.println(name+"哇哇哭...");
    }   
}
  • 构造代码块
    构造代码块的格式:

    {
    构造代码块
    }

注意: 构造代码块的大括号必须位于成员 位置上。

代码块的类别:
1. 构造代码块。
2. 局部代码块. 大括号位于方法之内。 作用:缩短局部 变量 的生命周期,节省一点点内存。
3. 静态代码块 static
构造 代码块要注意的事项:
1. java编译器编译一个java源文件的时候,会把成员变量的声明语句提前至一个类的最前端。
2. 成员变量的初始化工作其实都在在构造函数中执行的。
3. 一旦经过java编译器编译后,那么构造代码块的代码块就会被移动构造函数中执行,是在构造函数之前执行的,构造函数的中代码是最后执行 的。
4. 成员变量的显示初始化与构造代码块 的代码是按照当前代码的顺序执行的。

  • this关键字:

this关键字代表了所属函数的调用者对象。

this关键字作用:
1. 如果存在同名成员变量与局部变量时,在方法内部默认是访问局部变量的数据,可以通过this关键字指定访问成员变量的数据。
2. 在一个构造函数中可以调用另外一个构造函数初始化对象。

this关键字调用其他的构造函数要注意的事项:
1. this关键字调用其他的构造函数时,this关键字必须要位于构造函数中 的第一个语句。
2. this关键字在构造函数中不能出现相互调用 的情况,因为是一个死循环。

this关键字要注意事项:
1. 存在同名的成员变量与局部变量时,在方法的内部访问的是局部变量(java 采取的是“就近原则”的机制访问的。)
2. 如果在一个方法中访问了一个变量,该变量只存在成员变量的情况下,那么java编译器会在该变量的 前面添加this关键字。

this关键字调用其他的构造函数要注意的事项:
1. this关键字调用其他的构造函数时,this关键字必须要位于构造函数中 的第一个语句。
2. this关键字在构造函数中不能出现相互调用 的情况,因为是一个死循环。
还有就是注意:this不能用在static方法中!所以甚至有人给static方法的定义就是:没有this的方法!虽然夸张,但是却充分说明this不能在static方法中使用!

  • static关键字
    static静态变量(也称类成员)
    静态的成员变量只会在数据共享区中维护一份,而非静态成员变量的数据会在每个对象中都维护一份的。。
    它属于整个类所有,而不是某个对象所有,被类的所有对象所共享。
    静态成员可使用类名直接访问,也可以使用对象名进行访问。
    静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收。

static静态函数

1. static修饰成员变量 :如果有数据需要被共享给所有对象使用时,那么就可以使用static修饰。
    
    静态成员变量的访问方式:
            
            方式1: 可以使用对象进行访问。
                格式: 对象.变量名。
            
            方式二: 可以使用类名进行访问。
                格式: 类名.变量名;

        注意: 
            1. 非静态的成员变量只能使用对象进行访问,不能使用类名进行访问。
            2. 千万不要为了方便访问数据而使用static修饰成员变量,只有成员变量的数据是真正需要被共享的时候
            才使用static修饰。
        
    static修饰成员变量的应用场景: 如果一个数据需要被所有对象共享使用的时候,这时候即可好实用static修饰。

静态代码块:

static {
  /// 要执行的语句
}

静态代码块是在不管有没有创建对象,只要类被加载到内存中的时候执行的;
一个类的文件加载是懒加载,是在要使用到这个类的时候加载到内存中的,而静态代码块是在类文件加载的时候执行的

static(静态、修饰符)

static修饰成员变量时:static修饰成员变量时,那么该成员变量的数据就是一个共享的数据.

    静态成员变量的访问方式:
        
            方式一: 使用对象进行访问。
                    对象.属性名
            方式二:可以使用类名进行访问。
                    类名.属性名
    注意:
        1. 非静态成员变量不能类名直接访问,只能使用对象进行访问。
        2. 千万不要为了方便访问成员变量而使用static修饰,一定要是该数据是共享数据 时才使用static修饰。

static修饰方法(静态的成员方法):
    
    访问方式:
        
        方式一:可以使用对象进行访问。
                对象.静态的函数名();

        方式二:可以使用类名进行访问。
                类名.静态函数名字。
    
    推荐使用是类名直接访问静态的成员。

静态的成员变量与非静态的成员变量的区别:
1. 作用上的区别:
1. 静态的成员变量的作用共享一个 数据给所有的对象使用。
2. 非 静态的成员变量的作用是描述一类事物的公共属性。
2. 数量与存储位置上的区别:
1. 静态成员变量是存储方法 区内存中,而且只会存在一份数据。
2. 非静态的成员变量是存储在堆内存中,有n个对象就有n份数据。
3. 生命周期的区别:
1. 静态的成员变量数据是随着类的加载而存在,随着类文件的消失而消失。
2.非静态的成员数据是随着对象的创建而存在,随着 对象被垃圾回收器回收而消失。

静态函数要注意的事项:
1. 静态函数是可以用类名或者对象进行调用的,而非静态函数只能使用对象进行调用。
2. 静态的函数可以直接访问静态的成员,但是不能直接访问非静态的成员。
原因:静态函数是可以使用类名直接调用的,这时候可能还没有存在对象,
而非静态的 成员数据是随着对象 的存在而存在的。

3. 非静态的函数是可以直接访问静态与非静态的成员。
    原因:非静态函数只能由对象调用,当对象存在的时候,静态数据老早就已经存在了,而非静态
    数据也随着对象的创建而存在了。

4. 静态函数不能出现this或者super关键字。
    原因:因为静态的函数是可以使用类名调用的,一旦使用类名调用这时候不存在对象,而this
    关键字是代表了一个函数 的调用者对象,这时候产生了冲突。

静态的数据的生命周期:静态的成员变量数据是优先于对象存在的。

static什么时候修饰一个函数?

如果一个函数没有直接访问到非静态的成员时,那么就可以使用static修饰了。 一般用于工具类型的方法

静态函数不能访问非静态的成员?
静态函数只要存在有对象,那么也可以访问非 静态的数据。只是不能直接访问而已。

  • Java中的main()方法简单理解

在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是public static void 类型的,方法必须接收一个字符串数组的参数等等。

public static void main(String[] args) {
    System.out.println("Hello world");  
    }

这个main()方法的声明为:public static void main(String args[])。必须这么定义,这是Java的规范。

为什么要这么定义,和JVM的运行有关系。
当一个类中有main()方法,执行命令“java 类名”则会启动虚拟机执行该类中的main方法。

由于JVM在运行这个Java应用程序的时候,首先会调用main方法,调用时不实例化这个类的对象,而是通过类名直接调用因此需要是限制为public static。

对于java中的main方法,jvm有限制,不能有返回值,因此返回值类型为void。
main方法中还有一个输入参数,类型为String[],这个也是java的规范,main()方法中必须有一个入参,类细必须String[],至于字符串数组的名字,这个是可以自己设定的,根据习惯,这个字符串数组的名字一般和sun java规范范例中mian参数名保持一致,取名为args。

因此,main()方法定义必须是:“public static void main(String 字符串数组参数名[])”。

  • Java的单例设计模式

  • 单例模式的定义
    一个类有且仅有一个实例,并且自行实例化向整个系统提供。

  • 单例模式的作用
    简单说来,单例模式的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。

  • Java笔记-面向对象-封装_第1张图片
    单例设计模式类图.png
  • 代码体现:

(1)私有化构造函数

(2)创建私有并静态的本类的对象

(3)定义公有并静态的方法,返回该对象;

代码实现主要有两种方式:饿汉模式和懒汉模式:

饿汉模式:类加载的时候对象就已经存在,饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变

private Singleton() {
        
    }
    /// 由于是静态成员变量 -- 不管外界有没有使用, 当类加载到内存中时,就会创建一个对象
    private static Singleton singleton = new Singleton();
    
    public static Singleton getInstance() {
        return singleton;
    }

懒汉模式:类加载的时候对象还不存在,就是所谓的延迟加载方式,需要时再进行创建,懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的

    private Singleton() {
        
    }

    private static Singleton s = null;
    
    public static Singleton getInstance() {
        if (s == null) {
            s = new Singleton();
        }
        
        return s;
    }

懒汉式的线程不安全性,通常情况下,我们建议写饿汉式,因为是线程安全的
两个线程,线程一和线程二同时调用了getInstance方法,当线程1执行了if判断,single为空,还没来得及执行single =new Single()创建对象,这个时候线程2就来了,它也进行if判断,single依然为空,则创建Single对象,此时,两个线程就会创建两个对象,违背我们单例模式的初衷,如何解决呢?

出现线程安全的问题,为了解决这种问题,加入同步机制:静态同步函数的锁是类的字节码文件对象

单例模式总结:
1.单例模式是用来实现在整个程序中只有一个实例的。
2.单例类的构造函数必须为私有,同时单例类必须提供一个全局访问点。
3.单例模式在多线程下的同步问题和性能问题的解决。
4.懒汉式和饿汉式单例类。

你可能感兴趣的:(Java笔记-面向对象-封装)