类的编写(变量、方法、代码块、内部类)

Java知识点总结:想看的可以从这里进入

2.1 、类的编写

类(Class)是封装对象属性和行为的载体,是一个用于创建实例对象的模板,它将对象的共同特征和行为抽象出来,用成员变量表示事物的属性,用方法描述具体的行为,从而形成类。而通过new关键字对类实例化,即是创建了具有这种特征和行为的一个对象。

通常类具有五大成员:属性(成员变量)、方法、构造器、代码块、内部类。

package **;			//类所在的包
import **;			//导入其他的类
声明一个Person类,public为权限修饰符表示都可以访问,class关键字表示这是一个类,Person是类名
public class Person{
    /**
    *  类的属性(成员变量),一个人有哪些特征。人有姓名、有年龄、有性别........
    */
    private String name;  
	private int age;	  
  	private char gender;	 
    .........
    
    /**
    * 构造函数,每个类都有一个默认的无参构造函数,也可以自己定义,定义后默认的无参构造就不存在了
    * 构造函数可以用来对属性进行初始化
    */
    public Person(){}
    public Persion(String name, int age,char gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    /**
    * 实例代码块,也可以用来对属性进行初始化,它在创建对象时,在构造方法执行前执行
    */
    {
        name = "张三";
        age = 22;
        gender = '男'; 
    }
    
    /**
    *  内部类,在类中再定义一个类,此类作为外部类的一个组成部分
    */
    public class 类名{
        
    }
    
    /**
    * 成员方法,表示人可以说话
    */
    public void say(){
        System.out.printf("我叫%s,今年%d岁,性别%s",name,age,gender);
    }
}
2.1.1、变量
1、成员变量

Java对象的属性即是定义在类内部,方法外部的变量,其中通过static修饰的属性为类变量(final+static修饰的称为常量),没有static修饰的属性为成员变量。类的属性可以使任意数据类型。

  1. 类变量:由关键字static修饰,属于类,它在类被JVM加载时就分配空间,并指定默认的初始值,可以在不创建对象的情况下直接通过类名调用(对象也可以调用类变量,但是不推荐这么做,类变量最好还是通过类来调用)
    • static修饰:静态变量,可以不赋初值, 有默认值。在类加载的准备阶段先赋默认值,初始化阶段再赋正确值
    • static final修饰:是常量,必须赋初值,在类加载的准备阶段会直接赋正确值
  2. 实例变量:没有static修饰的,属于对象,可以不赋初值, 有默认值。只有在类实例化为对象时,才会分配空间,通过对象名调用
//属性的定义
权限修饰符 数据类型 变量名 =;		//成员变量
private int num = 15;

权限修饰符 static 数据类型 变量名 =;		//类变量
public static String str ;

权限修饰符 static final 数据类型 变量名 =;	//常量
public static final double PI = 3.14;

在这里插入图片描述

2、局部变量

在 Java 中,局部变量是定义在方法、构造器或代码块中的变量。它们只在定义它们的区域内可见可用。

  • 声明:局部变量在使用前必须声明。声明时,需要指定变量的类型和名称。不能被权限修饰符修饰
  • 初始化:在使用局部变量之前,必须显式的初始化初值。Java 编译器不会给局部变量一个默认值。

局部变量生命周期从它被初始化(赋值)时开始,到包含它的块执行完毕时结束。当块执行完毕后,局部变量所占用的内存将被回收。

  • 形参:定义在方法、构造器的()中,在整个方法内部都有效
  • 方法局部变量:定义在方法内部,方法运行至局部变量赋值那刻开始生效,到方法执行结束时失效
  • 代码块局部变量:定义在代码块内,代码块运行至局部变量赋值那刻开始生效,到代码块执行结束时失效
public class Test {
    public void method() {
        int num = 10; // 局部变量声明和初始化
        System.out.println(num); // 使用局部变量
    }
    
    public void anotherMethod(String name) {	//参数
        // System.out.println(num); // 错误:num是定义在method方法内的,在这里不可见
    }
}

局部变量和成员变量可以同名,如果方法中局部变量和成员变量同名时,局部变量会覆盖成员变量(就近原则),如果还想使用成员变量,需要配合this关键字。

3、区别

在Java中,成员变量和局部变量是两种常见的变量类型,它们在作用域、生命周期、初始化行为等方面有着明显的区别。

  1. 定义位置

    • 成员变量:字段(field),定义在类级别。可以是静态的(属于类)或非静态的(属于类的实例)。

    • 局部变量:定义在方法内、方法参数或者代码块内。只在定义它们的方法或代码块中有效。

  2. 初始化值

    • 成员变量:如果没有明确地初始化,Java会为它们提供一个默认值。

    • 局部变量:必须在使用前显式初始化,否则编译器会报错。Java不为局部变量提供默认值。

  3. 存储位置

    • 成员变量:存储在堆内存中,属于对象的一部分,而对象是在堆上分配的。

    • 局部变量:通常存储在栈内存中,访问速度很快。

  4. 生命周期

    • 成员变量:

      • 实例变量:生命周期与对象的生命周期相同。

      • 静态变量:其生命周期从类被加载开始到应用程序结束时为止。

    • 局部变量:生命周期仅限于方法调用的过程中。方法调用完成,局部变量所占用的内存就会被释放。

  5. 作用域

    • 成员变量

      • 实例变量:作用域是整个类内部,可以被类中的所有方法、构造器和特定的代码块访问。

      • 静态变量:可以被类的所有实例共享。

    • 局部变量:作用域仅限于声明它们的方法或代码块内。离开这个作用域后,它们将不再可用。

  6. 多线程

    • 成员变量:如果多个线程访问同一个对象的实例变量,可能会遇到并发问题。

    • 局部变量:因为每次方法调用都会为局部变量创建新的内存空间,所以局部变量本身是线程安全的。

4、新特性:var

JAVA 10后的一种新特性:使用var 定义局部变量,作用看起来类似与JavaScript 或 Python(实际上是不相同的),允许编译器利用其初始值来推断局部变量的类型。

  • Java是一种强语言,所以在定义变量时需要指定数据类型,而 Java10提供的var相当于一个动态类型,它定义的类型会由编译器自动推断出其初始的数据类型。
  • JavaScript 或 python属于弱类型语言,所以它们使用var时并没有明确的类型,但是Java即便使用var依然有明确的类型,它在指定初始值时,类型就确定下来了。

在这里插入图片描述

使用var时需要注意以下几点:

  1. var不是关键字,而是一个保留类型名,所以可以在代码中使用 var 作为变量名,但是不推荐这么做
  2. 使用 var 时,必须初始化变量,因为 var 需要通过变量的初始值来推断变量的类型(不能初始化为NULL值)
  3. 只能用于局部变量,不能用于类的成员变量、方法参数或返回类型。
  4. 不能使用 var 来推断泛型方法调用的类型参数。
  5. var不建议大范围使用,他会降低代码的可读性,建议在局部变量且初始值很容易确定的时候使用。

在这里插入图片描述

2.1.2、方法

方法是类中用来完成特定功能的代码片段,它是封装起来的一段段处理数据的逻辑代码,通过方法处理数据,得到最后的结果。它们是类和对象行为的核心部分。通过调用方法,可以实现代码的重用,提高程序的可维护性和可读性。

方法不能独立存在,必须属于类或者对象,在执行时由对象或类来调用(静态方法有类调用,非静态方法由对象调用)

方法一般为:构造方法、普通方法、main方法。其中main方法是程序的入口,由JVM调用。普通方法需要程序员去调用。构造方法是用来对一些属性等数据做一个初始化的处理,它是在new时被调用。

之所会设置方法这种结构,是为了解决软件开发中的几个重要问题:

  1. 封装和抽象:在没有方法的情况下,所有的逻辑都会堆积在一起,使得代码难以理解和维护。

  2. 代码重用:在软件开发中,经常会遇到需要重复执行相同任务的情况。没有方法,开发者可能会在多个地方复制和粘贴相同的代码,这导致代码冗余和维护困难。

  3. 模块化和组织:随着程序规模的增长,没有清晰的结构,代码很快就会变得混乱无章。

  4. 可维护性和可测试性:在大型软件项目中,确保代码的质量和寻找错误变得非常困难。没有一种有效的方式来隔离和测试代码的不同部分,测试和维护工作会变得非常耗时。

1、main方法

java中的main函数也叫作主函数,它主要作用一个程序的入口,可以运行,会被JVM识别并自动加载。格式是固定的:

public static void main(String[] args) {
}

public static void main(String[] 名字) 这个格式是固定的。

  • public static:程序在运行时执行的就是main方法,JVM通过 main 找到需要启动的运行程序,并检查main所在的类是否被加载,如果没有就进行加载,因此需要设置为 public static
  • void:main方法在执行完成后,程序运行就结束了,所以不需要返回值
  • String[](jdk5后也可以使用动态参数):用来运行时接受用户输入的参数。

main方法其实本质上也是一个 static 修饰的静态方法,除了还能作为程序的入口外,其他特性和普通静态方法一样。

2、构造方法

Constructor(构造器) 是类中一种特殊的方法,它只在对象创建的时候执行一次,用来进行初始化的一些操作类默认会有一个无参的构造方法(类至少都有一个构造器),也可以自己定义,但是一旦自己定义,默认的无参构造方法就不存在了,主要是方便对象的初始化。构造方法可进行重载,在同一个类中,通过传递的参数不同而选择相应的构造方法。

public class Test{
    public Test(){
        //这就是构造方法
    }
}
  • 特点
    • 方法的名字和类名完全相同,每个类有1个以上的构造方法,可以有0个以上参数。
    • 仅在创建对象时执行一次
    • 每个类会有一个默认的无参构造方法(前提是类没有任何其他构造器的时候),一旦自己定义了构造方法,默认的就不存在了
    • 默认无参构造方法在初始化时会把实例变量初始化为默认值,而有参构造会把实例变量按给定的参数值进行实例化。
    • 无返回值,也不能用void关键字(实际上构造方法返回的就是创建的实例对象)
    • 权限如果为私有,可以提供静态方法,将类创建为单例模式
    • 不能被static、final、synchronized、abstract、native修饰,不能有 return语句返回值
  • 作用
    • 结合new关键字创建实例对象,并初始化实例变量
    • 调用父类的构造方法
    • 分配内存空间存储对象,并返回指向该空间的引用
    • 执行其他操作
public class Student{
    private String name;
    //构造方法。无返回值,方法名字和类型相同
    public Student(String name){
        //在构造方法中对属性进行初始化操作
        this.name = name;
    }
    //定义了有参的构造方法,还想用无参的构造,就必须手动定义(构造器的重载)
    public Student(){ 
    }
}

构造方法是一种特殊的方法,因为必须和类名一致,子类是无法继承父类构造方法的,同时因为子类在创建实例时会先初始化父类的构造方法,所以在Java中的会有一个默认无参的构造方法,同时我们在创建类如果需要写有参构造方法,我们最好再提供一个无参的构造方法,方便类进行初始化的操作。

image-20220831153909097
3、成员方法

主要用来定义对象的行为或操作,使得对象可以执行特定的任务,它们是对象如何响应外部请求的主要方式。

  • 方法的定义

    //普通方法
    [修饰符] 返回值类型 方法名(参数列表){	//无返回值为void,有返回值则为相应的数据类型
        方法体;
        return 返回值;   //有返回值时,使用关键字return返回
    }
    /**
        1、修饰符:是可选的,可用的修饰符为public、protected、private、static、final、abstract
        2、返回值类型 :方法可能会有返回值。也可能没有返回值。有返回值时表明返回值类型,并使用return返回。没有时使用void关键字
        3、参数类型:调用方法时传递给方法的参数,此参数可以在方法体中使用。调用方法时传递的参数顺序要要一致
        4、方法体:方法体包含具体的语句,定义该方法的功能。
        5、关键字return:结束方法,返回方法的返回值(有返回值时不能使用void)
    *
    
  • 方法的调用:如果存在方法重载,即多个同名但参数不同的方法时,则根据传递的参数类型或个数不同而匹配不同的方法

    • 静态方法:static修饰的方法,类加载时就会产生(静态方法调用非静态方法时,需要先创建对象)
      • 类名.方法名();
    • 非静态方法:对象创建时才会产生(非静态方法可以直接调用静态方法)
      • 先实例化对象:类名 对象名 = new 类名();
      • 对象名.方法名();
    image-20220831154518662
4、方法递归

方法有一种自己调用自己的操作(还有一种是间接的:比如方法A调用方法B,方法B调用方法C,方法C再调用方法A),这种方式就是递归,通过自己调用自己从而形成一种循环的结构,所以和循环类似。

在使用递归时,首先我们要知道使用递归来解决什么问题,其次明确递归的结束条件,不能使其变成无限递归的死循环,最后要总结递归的等价关系式。(构造方法禁止使用递归)

递归的应用场景:

  • 阶乘问题

    public static void main(String[] args) {
        // 5的阶乘
        System.out.println(factorial(5));
    }
    /*
     * @description 使用递归解决阶乘问题
    */
    public static int factorial(int n){
        // 结束条件
        if (n == 1) {
            return 1;
        }
        //阶乘的关系式
        return n * factorial(n - 1);
    }
    
    image-20240411154352091
  • 二叉树深度

  • 汉诺塔问题

  • 斐波那契数列:1、1、2、3、5、8、13、21、34、55…

    public static void main(String[] args) {
       //斐波那契数列的每个数都是前两个数的和,除了前两个数。
       //F(0)=1, F(1)=1   F(n)=F(n−1)+F(n−2)
        for(int i=1 ; i<=10; i++){
            System.out.print(fibonacci(i)+",");
        }
    }
    /*
     * @description 斐波那契数列
    */
    public static int fibonacci(int n) {
        // 设置结束递归的限制条件
        if (n <= 2) {
            return 1;
        }
        // 递归步骤
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
    
    image-20240412145510166
  • 快速排序、归并排序(分治算法体现递归)

  • 遍历文件、解析xml文件等

    public static void main(String[] args) {
    	//递归获取文件夹的内容
        File f  = new File("d:/mydata");
        printFile(f);
    }
    public static void  printFile(File file) {
        // 获取指定文件夹内的所有文件和文件夹
        File[] files = file.listFiles();
        //先判断该文件是否正确
        if(files != null){
            //如果是文件,直接打印绝对路径.
            //如果是文件夹,则使用递归,获取该文件夹内的文件和文件夹
            for (File f : files) {
                // 是文件,直接打印
                if (f.isFile()) {
                    System.out.println("[" + f.getAbsolutePath()+"]");
                } else {
                    // 是文件夹:打印出文件夹后,使用递归获取文件夹内的内容
                    System.out.println("目录:" + f.getAbsolutePath());
                    printFile(f);
                }
            }
        }else {
            System.out.println("该路径不存在!!!!!");
        }
    }
    
    image-20230130122008820
  • ………….

5、参数

方法的()里接收的就是参数,它是一种将值或变量引用传递给函数或方法的机制。当一个方法被定义时,它可以被设计为接收参数,这些参数在方法执行时提供必要的输入数据。

  1. 基本类型参数:传递的参数为基本数据类型

  2. 引用类型参数:传递的参数为引用数据类型

  3. 变长参数(Varargs):在JDK5之后,还有一种可变的参数,它允许在调用方法时传入任意数量的参数。

    image-20210127161021129
    //传递可变参数,在方法中使用时和数组类似
    public void printMultipleNumbers(int... numbers) {
        for (int number : numbers) {
            System.out.println(number);
        }
    }
    
  4. 泛型参数:可以定义泛型参数,这样的参数类型在调用方法时才指定,提高了代码的重用性。

    //泛型,具体类型需要传递调用时确定
    public <T> void printElement(T element) {
        System.out.println(element);
    }
    
  5. Lambda表达式作为参数:Java 8 引入了Lambda表达式,可以传递一个Lambda表达式作为参数,通常是为了满足某个函数式接口的契约。

    public void useThread(Runnable action) {
        new Thread(action).start();
    }
    
2.1.3、代码块

在Java中,使用 { } 括起来的代码被称为代码块,但是代码块没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,是在加载类或创建对象的时候隐式调用(具体在JVM类加载的初始化阶段)。

代码块按定义的先后顺序依次执行,内部可以包括任何可执行语句:局部变量、调用其他对象方法、循环语句等等。

[修饰符] {
    代码块中的代码
}

根据代码块声明的位置和修饰符的不同,可以分为:

  • 局部代码块:定义在方法中的代码块,也叫本地代码块,比较少见。可以限定变量生命周期,及早释放,提高内存利用率。调用其所在的方法时执行

  • 静态代码块:在类中由static修饰,一般用于初始化静态成员变量,它随类加载而执行,并且只会执行一次。

  • 实例代码块:定义在类中的非静态代码块,用于初始化实例成员变量。在构造方法前执行(创建对象时执行)。

    实例代码块是在创建对象时按顺序隐式执行,所以多个代码块尽量合成一个,这样可以提高可读性

image-20230426191442623
  • 同步代码块:synchronized(obj){ } 内的代码块,一种多线程保护机制,被Synchronized关键词修饰的代码块会被加上内置锁,只有获得了这把锁的线程才访问,并且同一时刻,只有一个线程能持有这把锁,这样就保证了同一时刻只有一个线程能执行被锁住的代码。

    synchronized(Object) {
        // 由锁保护的代码
    }
    
    image-20220724184833258 image-20220724185018630

实例变量的初始化顺序:先执行实例初始化块或直接给实例变量执行初始值(这两种优先级相同,定义在后面的会覆盖掉定义在前面的),在执行在构造器中制定的初始值。

  • 实例初始化块在前,直接赋值在后,构造方法不指定初始值

    image-20230426192348852
  • 直接赋值在前,实例初始化块在后,构造方法不指定初始值

    image-20230426192410789
  • 三者都写

    image-20230426192447287
2.1.4、 内部类

一个类定义在另一个类内,那么这个类就是一个内部类(需要定义在类的 { } 内部才行,写在同一个源文件中互相独立的类并不是内部类),比如:在类A中定义一个类B,B就是内部类,而B可以当做A的一个成员看待。从种类上说,内部类可以分为四类:成员内部类、静态内部类、匿名内部类、局部内部类。

image-20220724161134268

1、成员内部类

成员内部类:最普通的一种内部类,定义在类中方法外的一个普通的类(没有static修饰),可用任意的访问修饰符修饰

  • 内部类可以访问外部类所有的属性和方法(包括私有)。但是外部类要访问成员内部类的属性和方法,必须要先实例化内部类

    image-20230202204614229
  • 在其他的类中使用内部类时,需要先有外部类的对象(成员内部类是依附外部类而存在的)

    image-20230203152306776
  • 关于在成员内部类中使用静态方法和属性的问题: JDK16后可以在非静态内部类中使用静态属性和方法的说明

    • 在 jdk16之前的版本,不能在非静态成员内部类中定义静态的属性和方法

      image-20230203153900340
    • 在Java16之后的版本是可以定义静态方法和属性的

      image-20230203153246101
  • 非静态内部类的方法访问变量时,首先在方法内查找是否存在该名字的局部变量,存在就使用,不存在则在内部类中查找是否存在该名字的成员变量,存在就使用,不存在则去外部类中找该名字的成员变量,存在就使用,不存在会出现错误,提示该变量不存在。

    当外部类变量与方法和内部类的同名时,内部类默认访问自己的成员变量或方法,而要引用外部变量时要加类名.this(类名.this.变量名、类名.this.方法名())

    image-20230202211913116
2、静态内部类

静态内部类:静态内部类就是在成员内部类加了一个 static 关键字,静态内部类就像外部类的一个静态成员一样,属于类而非对象。

  • 静态内部类不能直接访问外部类的非静态成员,但可以通过创建外部类的对象访问。

    image-20230203155334889
  • 外部类可以通过类名调用静态成员,或者使用静态内部类的对象访问内部类的实例成员

    image-20230429204209056
  • 外部类的静态成员与内部类的静态成员名称相同时,可通过(外部类名.静态成员) 访问外部类的静态成员

    image-20230203155654587
  • 静态内部类创建对象无需依赖外部类对象,可以直接创建

    image-20230203155915238
3、局部内部类

局部内部类:使用的比较少,声明在方法体或一段代码块的内部,仅在方法或代码块中生效。局部内部类不能再外部类方法以外的地方使用,所以局部内部类不能使用访问控制符和static 修饰。

  • 仅在定义的方法、代码块内部使用,其他地方无法使用(定义在方法内则只能在方法中使用,定义在for循环中则只能在for循环中使用),作用范围类似于局部变量

  • 局部内部类不可使用权限修饰符、静态(static)修饰符进行修,但可以使用final 或 abstract修饰。

    image-20230203160225947
  • 局部内部类可以直接访问方法中的属性

  • 局部内部类创建对象要在方法内部、局部内部类的外部声明。

    image-20230203160730189
  • 可以访问外部类的的属性和方法(包括私有的)。

    image-20230203161055270
4、匿名内部类

匿名内部类:很常用。它是一种没有名字的内部类,只能使用一次,通常是某个类在实现时只会使用一次,所以为了简化,不需要去专门写一个实现类,而是直接使用内部类完成。

  • 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
  • 在匿名内部类中可以使用外部类的属性,但是外部类却不能使用匿名内部类中定义的属性,匿名内部类没有名字,因此在外部类中无法获取这个类的类名,也就无法得到属性信息。(只能有一个对象)
  • 匿名内部类必须继承父类或实现接口,使用多态形式引用
new 接口/类名(参数1, 参数2...){
    实现方法1(){
        
    }
    实现方法2(){
        
    }
    ......
};

在线程的时候通常就喜欢使用匿名内部类实现

image-20220724171746400
  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值