《java练级之路》类和对象,博主熬夜肝六个小时万字博客

发布于:2022-11-12 ⋅ 阅读:(976) ⋅ 点赞:(0)

🎇🎇🎇作者:
@小鱼不会骑车
🎆🎆🎆专栏:
《java练级之旅》
🎓🎓🎓个人简介:
一名专科大一在读的小比特,努力学习编程是我唯一的出路😎😎😎
在这里插入图片描述

🙈🙈🙈作者心里话

小鱼一直都是秉承着“开开心心看博客,快快乐乐学知识的观点,虽然浏览量好低…”

前言
继上次博客,这次讲到的是类和对象的第三部分

🍃1. 封装

🍂1.2 封装的概念

面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说
就是套壳屏蔽细节。
生活案例:
生活中的封装比如ATM收款机,会使用一个机器来保证钱的正常流动,给你一个特定的方法来存钱取钱,不会说让你随意的取钱存钱,在java中,封装也是这个概念,封装就是把过程和数据包裹起来,对数据的访问只能通过已经定义的接口,面向对象计算始于这个基本概念,即现实世界已经被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象
java中封装的理解:
我们在程序设计的时候就追求“高内聚、低耦合

  • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
  • 低耦合:仅对外部暴露少量的方法用于使用。

隐藏对象内部的复杂性、只对外公开简单的接口。便于外界调用从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装的设计思想。

🍂1.3 访问限定符

  • 在Java中通过权限修饰符关键字private、protected和public实现封装

java中的四个访问修饰限定符
在这里插入图片描述
简单介绍一下这四个修饰限定符,后期会一一用到

  • public:可以理解为一个人的外貌特征,谁都可以看得到
  • default: 对于自己家族中(同一个包中)不是什么秘密,对于其他人来说就是隐私了
  • private:只有自己知道,其他人都不知道

default权限其实可以理解为默认权限,就是自己这其他三个权限都不设置的情况下,我们的编译器就会默认为default权限,例如

class Person {
    private String name;//名字
    public int age;//年龄
    int height;//体重,默认为default
}

其中我们的height就是default修饰的权限
注意:一般情况下成员变量设置为private,成员方法设置为public

🍂1.4 封装扩展之包

🍁1.4.1 包的概念

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件
包。有点类似于目录。比如我们为了更好的管理学习到的知识点,我们就可以用一个一个包,一种好的方式就是将相同属性的知识点放在相同文件下,也可以对某个文件夹下的知识点进行更详细的分装。
在这里插入图片描述

  • 在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,
  • 包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

🍁1.4.2 导入包中的类

Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.

public class Test1 {
    public static void main(String[] args) {
        java.util.Date date=new java.util.Date();
        //但是大家看上面代码是不是有一些冗余
        System.out.println(date);
    }

更简便的方法:可以使用 import语句导入包.

import java.util.Date;//用import导入这个util.Date这个包

public class Test1 {
    public static void main(String[] args) {
      Date date=new Date();
      //上述代码,代码量减少但是作用没变
        System.out.println(date);
    }
    
}

当然,如果我们需要util包中更多的类,我们就可以用import java.util.*

import java.util.*;//用import导入这个util.Date这个包中所有的类

public class Test1 {
    public static void main(String[] args) {
      Date date=new Date();
      //上述代码,代码量减少但是作用没变
        System.out.println(date);
    }
    
}

虽然我们这个是可以导入这个包中的任意类,就是我们用什么,他就默认导入什么类,但是不要和C语言的#include弄混了,C语言在预处理的时候将头文件中所有的代码都添加到了后缀为.i文件中,
但是java不一样,它并不会直接将那些包中的代码全部引入,只会是选择自己使用到的类进行引入。
但是需要注意的是java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配
在这里插入图片描述
那么就会报错,因为编译器是无法识别我的这个Date是哪个包里的,如果想解决这个问题,我们就只能使用完整的类名,这样就会解决编译器识别不清楚的问题。

import java.util.*;
import java.sql.*;

public class Test1 {
    public static void main(String[] args) {
      java.util.Date date=new java.util.Date();
        System.out.println(date);
    }

}

总结:

  • 两个及两个以上的类都可以用.*来引入包中的多个类
  • 如果是因为两个包中都有相同的类,那么就需要使用完整的类名来区分

import 和 C++ 的 #include 差别很大. C++ 必须 #include 来引入其他文件内容, 但是 Java 不需要,import 只是为了写代码的时候更方便.,import 更类似于 C++ 的 namespace 和 using

🍁1.4.3 自定义包

基本规则

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包中
  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 www.baidu.com经过颠倒后就是com.baidu.www).
  • 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储
    代码.
  • 如果一个类没有 package 语句, 则该类被放到一个默认包中.

如何创建一个包
第一步.

  1. 在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包
    在这里插入图片描述
    第二步
  2. 在弹出的对话框中输入包名, 例如 com.baidu.www
    在这里插入图片描述
    第三步
    3.生成一个java文件
    在这里插入图片描述
    4.此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了在这里插入图片描述
    并且我们也会发现Test.java,在文件的最上面出现了一个 package 语句
    在这里插入图片描述

🍁1.4.4 包的访问权限控制举例

Computer在我们的com.baidu.www的包里,Person在我们的com.Google.www的包里,两者不在同一个包
在这里插入图片描述
Computer类

package com.baidu.www;

public class Computer {
    private String cpu; // cpu
    String memory; // 内存
    public String screen; //屏幕
    public void  Boot() {
        System.out.println("开机");
    }

    public Computer(String cpu, String memory, String screen) {
        this.cpu = cpu;
        this.memory = memory;
        this.screen = screen;
    }
}

Person类

package com.Google.www;

import com.baidu.www.Computer;

public class Person {

    private String name;//姓名
    private int age;//年龄
    public void sleep() {
        System.out.println(name+"睡觉");
    }
    public void eat () {
        System.out.println(name+"吃饭");
    }
    public static void main(String[] args) {
        Computer computer=new Computer("HW", "i7", "8G");
        System.out.println(computer.cup);
        System.out.println(computer.memory);
        //报错,cpu是私有的是private修饰的,
        //memory是default修饰的,是只能在自己的包中使用的
    }
}
// 注意:如果去掉Computer类之前的public修饰符,代码也会编译失败!!!

🍁1.4.5 常见的包

  • java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  • java.lang.reflect:java 反射编程包;
  • java.net:进行网络编程开发包。
  • java.sql:进行数据库开发的支持包。
  • java.util:是java提供的工具程序包。(集合类等) 非常重要
  • java.io:I/O编程开发包。

🍃2. static

这部分是本节的重点

🍂2.1 再谈成员变量

我们创建一个学生类


public class Student {
    private String name;//姓名
    private int age;//年龄
    private String sex;//性别
    private String classroom;//教室
    public void sleep () {
        System.out.println(name+"上课");
    }
    public void eat () {
        System.out.println(name+"吃饭");
    }
public void print() {
    System.out.println(name+" "+age+" "+sex+" "+classroom);
}
    public Student(String name, int age, String sex, String classroom) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.classroom = classroom;
    }
    public static void main(String[] args) {
        Student s1=new Student("小明",18,"男","高三四班");
        Student s3=new Student("小王",17,"男","高三四班");
        Student s2=new Student("小张",19,"女","高三四班");
        s1.print();
        s2.print();
        s3.print();
    }
}

大家看,我们的教室是不是都一样,但是每次我们还需要再去填入是哪个教室,是不是很麻烦,并且大家也可以看到我们的变量都是开辟在堆上的
在这里插入图片描述
这三个对象里面都有相同的教室,是不是我们可以把这个教室单独拿出来,让这三个类都可以共享到,这个时候就可以使用static这个关键字了

🍂2.2 static修饰成员变量

在这里插入图片描述
被static修饰的成员变量是静态成员变量,不属于某个对象,是所有对象共享的。
那我们该如何单独访问这个静态成员变量呢?
试试用对象的引用访问
在这里插入图片描述
我们会发现,在之后并没有我们想要的classroom,这是因为我们的classroom是静态成员变量,不会出现在引用中,但是如果强行去调用也是可以的在这里插入图片描述
更标准一些的写法就是用类名去调用
在这里插入图片描述
其实我们的静态成员数据是不占内存的(随类的加载而加载),我们可以不用实例化就去调用

  public static void main(String[] args) {
       // Student s1=new Student("小明",18,"男");
        System.out.println(Student.classroom);
        //没有实例化依然可以调用
    }

同时我们还可以发现,我们的静态变量是不会储存在某个具体的对象中的
在这里插入图片描述

最后我们就可以总结静态成员变量的特性

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  3. 类变量存储在方法区当中
  4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)

只要被static修饰的变量就不属于对象

还有一个重要的知识点就是,我们去修改静态成员变量的值,但是每次实例化新对象后,接受的静态成员变量的值也都是被修改过的

class P{
    public static int a=0;

}
public class Test2 {
    public static void main(String[] args) {
        P p1=new P();
        P p2=new P();
        P p3=new P();
        p1.a+=5;
        p2.a+=4;
        p3.a+=2;
        p1.a-=8;
        System.out.println(P.a);
    }
    //最后输出3
    所以我们就把这个成员变量的权限设置为private以防被外界篡改信息

}

🍂2.3 static修饰成员方法

一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何
在类外访问呢?

public class Student {
    private String name;//姓名
    private int age;//年龄
    private String sex;//性别
    private static String classroom="高三四班";//教室
    public void sleep () {
        System.out.println(name+"上课");
    }
    public void eat () {
        System.out.println(name+"吃饭");
    }
public void print() {
    System.out.println(name+" "+age+" "+sex+" "+classroom);
}
    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
}
class Main {
    public static void main1(String[] args) {
        System.out.println(Student.classroom);
    }
    这里会报错,因为我的classroom的权限仅限于在类里面
}

那我们该如何访问呢?这时候就需要一个一个接口,注意!!!这个接口最好是被static修饰的,下面给大家解释
在这里插入图片描述
我们可以通过这个快速创建两个方法,用来修改值和返回值的

public class Student {
    private String name;//姓名
    private int age;//年龄
    private String sex;//性别
    private static String classroom;//教室
    public static String getClassroom() {
        return classroom;
    }
    public static void setClassroom(String classroom) {
        Student.classroom = classroom;
    }
}
class Main {

    public static void main(String[] args) {
            Student.setClassroom("高三四班");
        System.out.println(Student.getClassroom());
    }
  //  最后输出高三四班
}

前面讲了,我们的静态变量是不需要实例化对象的,可以直接使用,虽然我们无法直接修改classroom的值,但是我们可以通过输入接口来进行修改,但是大家有没有注意到,我们的这两个接口的方法都是被static修饰的。如果用的是非静态的方法,那我们还要再去实例化一个对象

public class Student {
    private String name;//姓名
    private int age;//年龄
    private String sex;//性别
 private static String classroom;//教室
	//没有用static修饰
    public  String getClassroom() {
        return classroom;
    }
	//没有用static修饰
    public  void setClassroom(String classroom) {
        Student.classroom = classroom;
    }
}
class Main {

    public static void main(String[] args) {
          Student student=new Student();//需要实例化对象才可以使用
            student.setClassroom("高三四班");
        System.out.println(student.getClassroom());
    }
}

那我们可以在静态方法中用静态成员变量,那我们可不可以用非静态成员变量呢?或者非静态的成员方法呢?

public class Student {
    private String name;//姓名
    private int age;//年龄
    private String sex;//性别
    private static String classroom;//教室

    public static String getClassroom() {
        return classroom;
    }
    public static void setClassroom(String classroom) {
        Student.classroom = classroom;
        System.out.println(name);
        System.out.println(age);
    }
}
class Main {

    public static void main(String[] args) {
        Student.setClassroom("高三四班");
    }
    //报错,无法从静态方法中引用非静态的成员数据
}

在这里插入图片描述
静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中无法传递this引用。

因为非静态成员的是属于对象的,我在调用静态方法时没有实例化任何对象,所以如果想访问非静态数据成员那就要实例化这个对象,用这个对象的引用去调用,就像我们的main函数,是静态方法,想在静态方法里面使用非静态的成员数据就需要实例化对象

   public static void main(String[] args) {
        Student.setClassroom("高三四班");
        Student student=new Student();
        //用这个引用去调用这个非静态对象
        //非静态成员变量。刚才已经修改了age权限所以现在可以用
        student.age=10;
        //非静态成员方法
        student.print();
    }
    这样就会避免报错

但是普通成员方法中可以调用静态方法!!!
总结静态方法

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
  3. 不能在静态方法中访问任何非静态成员变量

🍂2.4 static成员变量初始化

注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
就地初始化已经讲过了

  private static String classroom="高三四班";//教室

那什么是静态代码块初识化呢?

🍃3.代码块

🍂3.1 代码块概念以及分类

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

  • 普通代码块
  • 构造块
  • 静态块
  • 同步代码块(后续讲解多线程部分再谈)

🍂3.2 普通代码块

普通代码块:定义在方法中的代码块

public class Test4 {
    
    public static void main(String[] args) {
        {
                System.out.println("我是个普通代码块");
        }
         System.out.println("========我是分割符");
    }
最后输出
我是个普通代码块
我是分隔符
}
   public static void main(String[] args) {
        System.out.println("========");
        {
            System.out.println("我是普通代码块");
        }
    }
最后输出
我是分隔符
我是普通代码块

我们就可以发现我们的普通代码块是根据在方法中的顺序,从上往下执行的。当然,如果我们在普通代码块中创建局部变量,那么出了这个代码块,这个局部变量就会销毁
用法比较少见

🍂3.3 构造代码块

依旧是给大家对比一下我的代码块放在不同的位置输出的结果
在这里插入图片描述
由此我们可以总结到,我们的实例/构造代码块是在调用构造方法之后优先执行的, 一般就用于初始化实例化成员

🍂3.4 静态代码块

一般用于初始化静态成员变量。
什么是静态代码块呢?就是用static修饰的代码块,我们接下来进行两组对比
对比一
静态代码块放在构造方法的前面和后面
对比二
静态代码块和实例代码块进行对比,分别按不同的顺序

🍁3.4.1 对比一

在这里插入图片描述

总结: 静态代码块优先于构造方法执行,并且静态代码块根据先后顺序来绝对谁先执行

🍁3.4.2 对比二

在这里插入图片描述

总结: 不论先后顺序,静态代码块一定是在构造方法中第一个执行的,执行完静态代码块再去执行实例代码块,最后执行构造方法。

🍁3.4.3 注意事项

  • 静态代码块不管生成多少个对象,其只会执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  • 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
  • 实例代码块只有在创建对象时才会执行

网站公告

今日签到

点亮在社区的每一天
去签到