IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> ?【JavaSE基础语法】再探类与对象(封装等)!方块 -> 正文阅读

[Java知识库]?【JavaSE基础语法】再探类与对象(封装等)!方块

1.封装

1.1封装的概念

java做为一门面向对象的语言,具有基本的三大特性:封装,继承和多态。在类与对象的第二篇文章中我来讲解一下封装这一特性。这也是类和对象这个概念最直接的体现。何为封装?顾名思义,是将一堆部件(实体)组合在一起,再用某种方法将其封存,整体包装在一起。简单来说就是套壳屏蔽细节。比如我们生活中的汽车就是封装这种思想的体现。正常汽车的内部包含了很多零件,如发动机,底盘,离合器等。然而汽车的使用者并不需要
知晓每个部件的运行原理与使用方法,相反还可能损害零件。因此使用一个外壳将汽车套壳生产出来,保留一些接口与使用者进行交互,如方向盘,刹车等等。
同样的对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
在这里插入图片描述

封装的通俗定义:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互。java实现封装:类+访问权限。

1.2访问限定符

不难得出结论,类这一概念可以将数据和操作数据的方法进行有机结合,既字段和成员方法。那么我们还需要访问权限来达到隐藏对象属性和实现细节这一目的。因此需要使用访问限定符,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:

privatedefaultprotectedpublic
同一包中同一类
同一包中不同类
不同包中的子类
不同包中非子类

通过上表我们可以发现四种修饰符基本是按照其名字的含义来进行使用范围界定的。其中private指私有的,也是访问权限要求最高的,只有同一类中可以使用。而default为默认权限,在使用类时如果不使用修饰符则视为default,只能在同一个包中进行使用。protected多应用于继承场景,可以用于继承的子类中。而public则意为公共的,都可以访问的。
访问权限不仅可以限定成员的可见性,也可以限定类的可见性。
一般情况下设置成员变量为private,成员方法为public。

1.3 封装之–包

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。同样的,java中的包就像一个专门的仓库,比如一个武器库,里面存放的是各种各样的武器,既他们都有共同的一个特点。包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
那么如何导入包中的内容呢?下面介绍三种方式。第一种方式是显式的指定全路径的包名,在java中已经写好了很多类供我们使用,比如Date类。使用java.util.Date即可调用:

public class Test {
 public static void main(String[] args) {
 java.util.Date date = new java.util.Date();//ci此处注意一定要写全路径
 // 得到一个毫秒级别的时间戳
 System.out.println(date.getTime());
 }
 }

第二种方法相对简单,直接使用import导入Date类,然后可以直接定义对象:

import java.util.Date;
public class Test {
 public static void main(String[] args) {
 Date date = new Date();
 // 得到一个毫秒级别的时间戳
 System.out.println(date.getTime());
 }
}

如果需要使用 java.util 中的其他类, 可以使用 import java.util.*。此符号表示已经导入util包中的所有类。但是使用此语句可能会出现冲突的情况,既在两个包中存在相同名的类,那么在调用时会出错:

import java.util.*;
import java.sql.*;
public class Test {
 public static void main(String[] args) {
 // util 和 sql 中都存在一个 Date 这样的类, 此时就会出现歧义, 编译出错
 Date date = new Date();
 System.out.println(date.getTime());
 }
}
// 编译出错
Error:(5, 9) java:Date的引用不明确
 java.sql 中的类 java.sql.Date 和 java.util 中的类 java.util.Date 都匹配

所以建议初学者在调用包时采用显式的定义方式,写全名称不要偷懒。上图的代码需要补全具体的包名与类名:

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

第三种方式我们也可以使用import static导入包中静态的方法和字段:

import static java.lang.Math.*;
public class Test {
 public static void main(String[] args) {
 double x = 30;
 double y = 40;
 
 // 静态导入的方式写起来更方便一些.
 // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
 double result = sqrt(pow(x, 2) + pow(y, 2));
 System.out.println(result);
 }
}

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

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包码在哪个包中.
  • 包名需要尽量指定成唯一的名字。
  • 包名要和代码路径相匹配. 例如创建 day0925.demo1 的包, 那么会存在一个对应的路径 day0925/demo1 来存储代码.
  • 如果一个类没有 package 语句, 则该类被放到一个默认包中。

实现步骤如下:
1.在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包。在弹出的对话框中输入包名, 例如day0925.demo01.
在这里插入图片描述

2.在新建的包中,定义自己的类,如Test类。
3.此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了:
在这里插入图片描述
同时在包中的Test.java文件中第一句会自动出现一条package语句:package day0925.demo01。
常见的包如下

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

2.类中的静态(static)成员

2.1静态成员变量

当我们在定义类时,首先会去定义成员变量。成员变量是用来描述对象的属性。比如下面给出的动物类:

public class Animal{
String name;
int age;
String color;
String ancestor;
}

其中,名称,年龄以及颜色(毛发)都是用来描述具体所创建的对象。比如我们定义猫这个对象,它就会拥有自己的这三个字段。换言之,每个对象都会有属于自己的独立的成员变量。但是祖先这个变量是用来描述所有动物的,它是广义的说法。显然不适合以这种方式来定义。因此,当我们想要定义一个成员变量来描述类时,而非描述一个具体的对象,这个属性是属于类的,我们只需将这个字段用static来修饰即可。
static修饰的成员变量称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。具有以下的特性:

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
  2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  3. JDK7及以前,HotSpot(Java虚拟机)中存储在方法区,JDK8及之后,类变量存储在Java堆中
  4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)。
public class Animal{
String name;
int age;
String color;
String ancestor=“cell”
public static void main(String[] args){
Animal cat=new Aniaml();
System.out.println(cat.ancestor);
System.out.println(Animal.ancestor);
}

以上代码可以证明一些特性。

2.2static修饰成员方法

在我们之前的学习中,类中的成员变量都是需要加修饰符的,可以是private,也可以是public。**那么使用static修饰的成员变量应该如何在类外访问呢?**先来看以下代码:

public class Student{
 private String name;
 private String gender;
 private int age;
 private double score;
 private static String classRoom = "Bit306";
 
 // ...
}
public class TestStudent {
 public static void main(String[] args) {
 System.out.println(Student.classRoom);
 }
}
编译失败:
Error:(10, 35) java: classRoom 在 extend01.Student 中是 private 访问控制

在Java中,**被static修饰的成员方法称为静态成员方法,**是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。

public class Student{
 private static String classRoom = "Bit306";
 public static String getClassRoom(){
 return classRoom;
 }
}
public class TestStudent {
 public static void main(String[] args) {
 System.out.println(Student.classRoom);
 }
}
//输出结果
Bit306

静态方法的特性

  1. 不属于某个具体的对象,是类方法
  2. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
  3. 静态方法没有隐藏的this引用参数,因此不能在静态方法中访问任何非静态成员变量
  4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用。
  5. 静态方法无法重写,不能用来实现多态。

2.3static成员变量的初始化

我们前面学习了使用构造方法来初始化非静态的成员变量。但是静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性。静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。

2.3.1就地初始化

该种方法顾名思义,在定义变量的时候直接给出初始值。

public class Student{
 private String name;
 private String gender;
 private int age;
 private double score;
 private static String classRoom = "Bit306"; 
}

2.3.2 静态代码块初始化

要了解此形式,我们需要先了解什么是代码块。我们且往下慢慢道来。

3.代码块和内部类

3.1代码块

3.1.1代码块分类

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

  • 普通代码块
  • 构造块
  • 静态块
  • 同步代码块
    下来我们按顺序进行讲解。

3.1.2普通代码块

第一个是普通代码块,即出现在方法内部的代码块。这种用法是比较少见的。

public class test{

public void method{
{
System.out.printlin("这是一个代码块");
}
}
}

3.1.2构造代码块

构造(代码)块:**定义在类中的代码块(不加修饰符)。也叫:实例代码块。**构造代码块一般用于初始化实例成员变量。

public class Student{
 //实例成员变量
 private String name;
 private String gender;
 private int age;
 private double score;
 
 public Student() {
 System.out.println("I am Student init()!");
 }
 
 //实例代码块
 {
 this.name = "bit";
 this.age = 12;
 this.sex = "man";
 System.out.println("I am instance init()!");
 }

实例代码块优先于构造方法执行,因为编译完成后,编译器会将实例代码块中的代码拷贝到每个构造方法第一条语句前。而就地初始化的语句优先于代码块中的。既顺序为:就地初始化语句—代码块语句–构造方法语句。

3.1.3静态代码块

使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。和上方的构造块区别为在代码块前加上static修饰符。代码如下:

package day0925.demo01;

public class Test {
    private String name;
    private String gender;
    private int age;
    private double score;
    private static String classRoom;
    //实例代码块
    {
        this.name = "bit";
        this.age = 12;
        this.gender = "man";
        System.out.println("I am instance init()!");
    }
    // 静态代码块
    static {
        classRoom = "bit306";
        System.out.println("I am static init()!");
    }
    public Test(){
        System.out.println("I am Student init()!");
    }
    public static void main(String[] args) {
        Test s1 = new Test();
        Test s2 = new Test();
    }
}

运行结果:
在这里插入图片描述

注意事项:

  • 静态代码块不管生成多少个对象,其只会执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的。Java代码在经过编译器编译之后,如果要运行必须先要经过类加载子系统加载到JVM中才能运行。在链接阶段第二步准备中会给静态成员变量开辟空间,并设置为默认值,在初始化阶段,会执行静态代码块中的代码。(了解:关于类加载过程后序JVM中会详细讲解)。
  • 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次合并,最终放在生成的clinit方法中,该方法在类加载时调用,并且只调用一次。而实例代码块只有在构造对象的时候会使用。

3.2内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,称为内部类。内部类也是封装的一种体现。 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类。

public class A{
 
}
class B{
 
}
// A 和 B是两个独立的类,彼此之前没有关系

注:内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件。内部类可以定义在类或者方法内的任何位置。包括代码块内。根据内部类定义的位置不同,一般可以分为以下几种形式:

  1. 成员内部类(普通内部类:被static修饰的成员内部类。 静态内部类:未被static修饰的成员内部类)
  2. 局部内部类(不谈修饰符)、匿名内部类
    注意:内部类其实日常开发中使用并不是非常多,大家在看一些库中的代码时候可能会遇到的比较多,日常开始中使用最多的是匿名内部类。

3.2.1成员内部类

在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类。
普通内部类:即未被static修饰的成员内部类。

public class OutClass {
 private int a;
 static int b;
 int c;
 public void methodA(){
 a = 10;
 System.out.println(a);
 }
 public static void methodB(){
 System.out.println(b);
 }
 // 成员内部类:未被static修饰
 class InnerClass{
 int c;
 public void methodInner(){
 // 在内部类中可以直接访问外部类中任意访问限定符修饰的成员
 a = 100;
 b =200;
 methodA();
 methodB();
 // 如果外部类和内部类中具有相同名称成员时,优先访问的是内部类自己的
 c = 300;
 System.out.println(c);
 // 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
 OutClass.this.c = 400;
 System.out.println(OutClass.this.c);
 }
 }
 public static void main(String[] args) {
 // 普通内部类:对象创建 以及 成员访问
 OutClass outClass = new OutClass();
 System.out.println(outClass.a);
 System.out.println(OutClass.b);
 System.out.println(outClass.c);
 outClass.methodA();
 outClass.methodB();
 System.out.println("==========================");
 // 要访问普通内部类中成员,必须要创建普通内部类的对象
 // 而普通内部类定义与外部类成员定义位置相同,因此创建普通内部类对象时必须借助外部类
 // 创建内部类对象
 OutClass.InnerClass innerClass1 = new OutClass().new InnerClass();
 // 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建内部类对象
 OutClass.InnerClass innerClass2 = outClass.new InnerClass();
 innerClass2.methodInner();
 }
}

注意事项:

  1. 外部类中的任何成员都可以被在普通内部类方法中直接访问
  2. 普通内部类所处的成员与外部类成员位置相同,因此也受public、private等访问限定符的约束
  3. 在内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
  4. 普通内部类对象必须在先有外部类对象前提下才能创建
  5. 普通内部类的非静态方法中包含了一个指向外部类对象的引用。
  6. 外部类中,不能直接访问内部类中的成员,如果要访问必须先创建内部类对象。
    对于第五点,我们可以从字节码层面进行说明。
    在这里插入图片描述
    静态内部类:
    被static修饰的内部成员类称为静态内部类。
public class OutClass {
 private int a;
 static int b;
 public void methodA(){
 a = 10;
 System.out.println(a);
 }
 public static void methodB(){
 System.out.println(b);
 }
 // 静态内部类:被static修饰的成员内部类
 static class InnerClass{
 public void methodInner(){
 // 在内部类中只能访问外部类的静态成员
 // a = 100; // 编译失败,因为a不是类成员变量
 b =200;
 // methodA(); // 编译失败,因为methodB()不是类成员方法
 methodB();
 }
 }
 public static void main(String[] args) {
 // 静态内部类对象创建 & 成员访问
 OutClass.InnerClass innerClass = new OutClass.InnerClass();
 innerClass.methodInner();
 }
 }

注意事项:

  1. 在内部类中只能访问外部类中的静态成员
  2. 创建内部类对象时,不需要先创建外部类对象
  3. 成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:外部类名称$内部类名称。

3.2.2局部内部类

定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。

public class OutClass {
 int a = 10;
 public void method(){
 int b = 10;
 // 局部内部类:定义在方法体内部
 // 不能被public、static等访问限定符修饰
 class InnerClass{
 public void methodInnerClass(){
 System.out.println(a);
 System.out.println(b);
 }
 }
 // 只能在该方法体内部使用,其他位置都不能用
 InnerClass innerClass = new InnerClass();
 innerClass.methodInnerClass();
 }
 public static void main(String[] args) {
 // OutClass.InnerClass innerClass = null; 编译失败
 }
}

注意事项:

  1. 局部内部类只能在所定义的方法体内部使用。
  2. 不能被public、static等修饰符修饰。
  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$x内部类名字.class,x是一个整数。
  4. 几乎不会使用。
    关于匿名内部类,在后续介绍接口时,我会详细的进行描述。
    至此,关于类和对象的内容基本介绍完毕。如有不足欢迎大家的批评与指正。谢谢大家。

作者:端履门没有门

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-10-06 12:04:30  更:2021-10-06 12:08:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 10:54:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码