JAVA学习(7)(初始化、构造方法、封装、包、自定义包、常见的包、static修饰成员变量、static修饰成员方法、static成员变量初始化、代码块​)

接上次的博客:Java学习(6)初识二维数组、类的初认识_di-Dora的博客-CSDN博客

目录

初始化

声明时初始化(就地初始化)

构造方法初始化

构造方法(通过构造方法对成员变量进行初始化)

封装

自定义包

常见的包

static修饰成员变量

static修饰成员方法

static成员变量初始化

代码块

一些易错题练习:


初始化

在Java中,初始化是指在创建对象时,为对象的成员变量分配初始值的过程。

在Java中,局部变量在使用前必须先初始化,否则会编译错误。局部变量可以在声明时初始化,也可以在后面的代码中初始化。如果不进行初始化,Java编译器是不认可的。

public class Test {
    public void testMethod() {
        int num1 = 10; // 初始化声明
        int num2; // 声明
        num2 = 20; // 后面初始化
        String str; // 声明
        str = "Hello"; // 后面初始化
        boolean flag = true; // 初始化声明
        char ch; // 声明
        ch = 'a'; // 后面初始化
        Object obj; // 声明
        obj = null; // 后面初始化
    }
}

在上面的示例代码中,变量num1、flag、ch和obj都是在声明时进行了初始化,变量num2和str则是在后面的代码中进行了初始化。如果使用了未初始化的局部变量,编译器会报错。

public void test() {
    int num; 
    boolean flag;
    String str; 
    int[] arr; 
    // 使用局部变量
    System.out.println(num);
    System.out.println(flag);
    System.out.println(str);
    System.out.println(arr);
}

在上面的代码中,变量num、flag、str和arr都没有被初始化,编译器会报错。

d2a04285043b455aa2cfc962d805a160.png

Java中的实例变量的初始化可以分为两种:声明时初始化构造方法初始化

声明时初始化(就地初始化)

可以在成员变量声明时,直接为其指定一个初始值。例如:

public class MyClass {
    int a = 0; // 声明时初始化
    String str = "Hello World"; // 声明时初始化
}

上面的例子中,我们为MyClass类的成员变量a和str指定了初始值。这样,在创建MyClass类的对象时,这些成员变量就会被初始化为指定的初始值。

构造方法初始化

除了在声明时初始化,我们还可以在构造方法中初始化成员变量。例如:

public class MyClass {
    int a;
    String str;

    public MyClass( ) { // 构造方法初始化
        a = 34;
       str = "welcome!";
    }

    
    public MyClass(int a, String str) { // 构造方法初始化
        this.a = a;
        this.str = str;
    }


    public static void main(String[] args) {
        MyClass myclass=new MyClass();
        MyClass myclass2=new MyClass(34,"welcome!");
    }
}

上面的第二个例子中,我们为MyClass类定义了一个构造方法,该构造方法接收两个参数a和str,并分别为成员变量a和str赋值。在创建MyClass类的对象时,我们可以调用这个构造方法,通过传递参数来初始化成员变量。

需要注意的是,在Java中,成员变量有默认初始值,如果没有指定初始值,就会使用默认初始值进行初始化。整型默认值为0,浮点型默认值为0.0,布尔型默认值为false,引用类型默认值为null。

下面我们详细说说构造方法。

构造方法(通过构造方法对成员变量进行初始化)

在Java中,构造方法是一种特殊的方法,用于创建对象并为对象的成员变量分配初始值。构造方法与类同名,没有返回值类型,但可以有参数。

当我们创建一个对象时,Java会自动调用相应的构造方法来完成对象的初始化工作。如果我们没有定义构造方法,Java会提供一个默认的构造方法,该构造方法不接收任何参数并且不做任何操作。

以下是一个简单的示例,演示如何创建一个具有构造方法的Java类:

public class Person {
    private String name;
    private int age;
    
    // 构造方法
    public Person() {
        name = "Andi";
        age = 34;
    }
    
    public Person(string name,int age) {
        this.name = name;
        this.age = age;
    }


    public static void main(String[] args) {
        Person s =new Person();
        Person p =new Person("Tom",18);
    }
}

在上面的例子中,我们定义了一个名为Person的类,并且定义了一个构造方法Person(String name, int age)。在创建Person类的对象时,我们可以调用这个构造方法。

需要注意的是,构造方法可以有多个重载形式,但它们必须具有相同的名称且等于类名。如果我们没有定义任何构造方法,Java会自动提供一个默认的构造方法,该构造方法不接收任何参数并且不做任何操作。如果我们定义了一个或多个构造方法,但没有定义默认构造方法,那么在创建对象时,必须调用其中的一个构造方法。

再强调一遍:

构造方法的名字与类名相同;

没有返回值(设置为void也不可以!);

可以有参数,即构造方法之间是可以构成“方法的重载”(方法名相同,作用类似,参数不同);

当调用完成构造方法之后,对象才产生了,构造方法在整个对象的生命周期只执行一次。

当一个类中没有任何一个构造方法的时候,JAVA编译器会自动提供一个不带参数的构造方法。

当一个类中,存在任何一个构造方法时,JAVA就不会自动提供构造方法。

构造方法只是初始化,它不会创建空间。完成一个对象的构造分两步:1、分配内存空间;2、调用合适的构造方法。

可以通过this调用其他构造方法来简化代码。

假设有一个Person类,包含三个属性:姓名name、年龄age、性别gender。我们可以通过this关键字来简化构造方法的实现。比如:

public class Person {
    private String name;
    private int age;
    private String gender;

//1
    
public Person(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

//2

public Person(String name) {
        this(name, 0, null); // 调用有三个参数的构造方法,其他参数使用默认值
    }

//3
public Person() {
        this(null); // 调用有一个参数的构造方法,其他参数使用默认值
    }
}

 在上面的示例中,我们使用了this关键字来调用其他构造方法。2调用了1的构造方法,3调用了2的构造方法。在第一个构造方法中,this指代当前对象,用于访问当前对象的属性。在后面两个构造方法中,this(参数)的形式用于调用其他构造方法,以避免重复的代码。需要注意的是,this(参数)只能在构造方法内部使用,而且只能放在第一行。

此外,我们也可以使用this关键字来访问当前对象的属性和方法。例如:

public class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    public void printInfo() {
        System.out.println("Name: " + this.name);
        System.out.println("Age: " + this.age);
    }

    public void eat() {
        System.out.println(this.name+"正在吃饭");
        this.printInfo();
    }

}

在上面的示例中,我们使用this关键字来访问当前对象的属性name和age,并且使用this.printInfo()来调用当前对象的printInfo()方法。

需要注意的是,this关键字只能在实例方法(非静态方法,稍后会讲到)中使用

同时,如果在构造方法中使用this关键字调用其他构造方法时,不能形成环,即不能形成循环调用。

比如:

public class Person {
    private String name;
    private int age;
    private String gender;



public Person(String name) {
        this(); 
    }

public Person() {
        this("Cindy"); 
    }


}

再强调一遍:

this 本身代表的是当前对象的引用;

this( ):调用当前类当中的其他构造方法(匹配参数),只能在当前构造方法内部使用!只能放在第一行!

this.data :访问当前对象的属性;

this.func( ) :调用当前对象的方法。

注意!不能形成环!

封装

封装是面向对象编程中的一个重要概念,它指的是将对象的状态和行为封装在一个单元内部,并且只向外部暴露必要的接口来与对象进行交互,而隐藏其内部的实现细节。换句话说,就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅仅对外公开接口与对象进行交互。

咱们先来展开来谈一谈封装在面向对象编程中扮演着的重要的角色,它具有以下几个主要作用:

1、数据隐藏和安全性:封装可以将类的数据(属性)隐藏在类的内部,只允许通过类的公有接口(方法)进行访问。这样做的好处是,外部代码无法直接修改类的内部数据,从而保护了数据的安全性。只有通过类提供的方法来访问和操作数据,可以实现对数据的控制和验证,避免了数据被错误地修改或篡改。

2、代码模块化和解耦合:封装可以将一组相关的数据和操作封装在一个类中,形成一个独立的代码模块。这种模块化的设计使得代码更加清晰、可读性更高,并且有利于团队协作开发。此外,通过封装,不同的模块之间可以相互独立,彼此之间的实现细节和变化不会对其他模块产生影响,实现了代码的解耦合,提高了代码的可维护性和可扩展性。

3、隐藏实现细节:封装可以将类的内部实现细节隐藏起来,只对外部提供抽象的公有接口。这样做的好处是,当类的内部实现发生变化时,不会对外部代码产生影响,只需保持公有接口的兼容性即可。这种隐藏实现细节的方式称为信息隐藏或接口隔离,它可以有效地保护代码的稳定性和可维护性。

4、提供更好的代码组织和管理:封装使得代码更加有组织性,可以将相关的属性和方法组织在一个类中。这样可以使代码更加易于理解、维护和管理。封装还可以提供一定的抽象层次,隐藏了底层实现细节,使得代码更加简洁和高效。

总的来说,封装通过隐藏数据、提供公有接口、模块化代码等方式,提供了一种良好的编程实践,它可以增加代码的安全性、可读性、可维护性和可扩展性。封装是面向对象编程的核心原则之一,对于构建可靠、可复用和可扩展的软件系统至关重要。

在Java中,封装可以通过访问修饰符来实现。

在 Java 中,有四种访问限定符(access modifiers)用于限制对类、方法和变量的访问权限,分别为:
private:表示私有访问,只有在当前类内部才可以访问该成员,其他类无法访问;

default(也称为包访问权限):没有指定任何访问修饰符时,默认为 default 访问权限,表示只有在同一包内的类才能访问该成员。

protected:表示受保护访问,只有在当前类内部和同一包内的类以及该类的子类中才能访问该成员;

public:表示公共访问,任何类都可以访问该成员;


访问修饰符是指用于修饰类、方法和变量的关键字,包括 public、private、protected、abstract、static、final、synchronized 等等,不同的修饰符有不同的作用。例如:

public:表示公共访问,任何类都可以访问该成员;
private:表示私有访问,只有在当前类内部才可以访问该成员,其他类无法访问;
protected:表示受保护访问,只有在当前类内部和同一包内的类以及该类的子类中才能访问该成员;
abstract:表示抽象的,只能用于抽象类和抽象方法中,不能直接实例化抽象类,也不能在抽象类中实现抽象方法;
static:表示静态的,静态变量和静态方法属于类,不属于实例,可以直接使用类名访问;
final:表示不可修改的,用于修饰变量和方法,表示变量只能被赋值一次,方法不能被子类重写;
synchronized:表示同步的,用于修饰方法和代码块,可以保证多个线程的同步访问,避免数据不一致的情况发生。

通过使用访问修饰符,我们可以控制对象对外部的可见性,防止外部对象直接修改对象的状态,从而提高程序的安全性和可维护性。

假设我们有一个Person类,它包含两个私有成员变量name和age,并且提供了公共的getName()和getAge()方法来获取这些成员变量的值。这样,外部对象只能通过这些公共方法来访问Person对象的状态,而不能直接访问其成员变量。如果我们需要修改Person对象的状态,可以提供公共的setName()和setAge()方法来进行修改,同时在这些方法中添加必要的校验逻辑,以确保对象的状态是合法的。

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name != null && !name.trim().equals("")) {
            this.name = name;
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        }
    }
}

在上面的代码中,name和age成员变量被声明为私有,外部对象无法直接访问它们。同时,我们提供了公共的getName()和getAge()方法来获取这些成员变量的值,以及公共的setName()和setAge()方法来设置这些成员变量的值。在这些公共方法中,我们添加了一些校验逻辑,以确保对象的状态是合法的。这样,我们就实现了对Person对象的封装,只向外部暴露必要的接口,同时隐藏了对象的实现细节,提高了程序的安全性和可维护性。

但是,如果我们的要初始化的实例变量很多怎么办?

不用担心,JAVA提供了很灵活的Getter和Setter自动生成设置,你只需要单击鼠标右键,按照以下步骤,轻动几下手指就可以了!

db26543ef38e4604ba3a413899266d01.png

按住CTRL一个一个勾选可以全部选上,你也可以根据自己的需求来勾选:  

11273e3cdf1c4577bf2b124fd2301b89.png

public class Student {
    public String name;
    public int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 构造方法也一样:2627b6dda7e64dc9a441396b31252a49.png

只要带一个参数的: 

964b6eda664c47deb94bd2aeaa492b3c.png

public class Student {
    public String name;
    public int age;

    public Student(String name) {
        this.name = name;
    }
}

两个参数都要: 

 e85225e511954cd09c9b9a52f455cbde.png

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

 但是它不可以生成不带参数的!!!37328a71d0f04b9b8be8417fcd1eb169.png

No范围privatedefault(默认)protectedpublic
1同一个包中的同一类可以可以可以可以
2同一个包中的不同类可以可以可以
3不同包中的子包可以可以
4不同包中的非子类可以

在Java中,包是一种组织类和接口的方式,它将相关的类和接口组织在一起,以便更好地管理和维护。一个包包含一个或多个类和接口,以及可选的子包。通过使用包,我们可以避免类名冲突,使代码更加模块化和可复用。

Java中的访问权限共有4种:public、protected、private和default(又称为包访问权限)。其中,default是最不严格的一种访问权限,它指定的是同一个包内的其他类可以访问该类的成员,而其他包的类则无法访问该类的成员。默认权限在Java代码中可以不显式地指定,如果没有指定访问权限,则默认为default权限。

例如,我们有一个类Person,在同一个包下还有一个类Student,如果Person类的成员属性和成员方法没有指定访问权限,则默认为default权限,Student类就可以访问Person类的成员,如下所示:

// Person.java
package com.example;

public class Person {
    String name; // default权限

    void sayHello() { // default权限
        System.out.println("Hello");
    }
}

// Student.java
package com.example;

public class Student {
    void study(Person person) {
        System.out.println(person.name);
        person.sayHello();
    }
}

在Java中,包是一种用于组织和管理类的机制。它们提供了一种命名空间,以防止类名冲突,并且使代码更易于维护和管理。即为了更好的管理类,把多个类收集在一起成为一组,称为软件包。

包也可以用来控制访问级别和可见性。

包可以包含其他包和类。包名称使用点号分隔,例如 java.util 和 java.lang 。

65ba5a41cd214c94ad053de98c58e334.png

包名称通常与文件夹结构相对应,也就是说,包名称对应于源代码文件的目录结构。

使用包的好处包括:

  • 防止类名冲突:每个包都有自己的命名空间,避免了类名的冲突。(同一个工程里允许存在相同名称的类,只要处在不同的包中即可)
  • 组织和管理类:将类组织成逻辑上相关的包,使得代码更易于维护和管理。
  • 访问控制:包可以用于控制类和成员的访问级别和可见性。

在Java中,每个源代码文件都应该包含一个package语句,用于指定该文件中定义的类所属的包。例如:

package com.example.myapp;

 通过import导入包中的类:

import java.util.Arrays;
class  Test2{
    public static void arr(){
        int [] array={1,2,3,4,5};
        System.out.println(Arrays.toString(array));
    }
}

我们可以一直带着它的包的名字走,比如这样:

        java.util.Date date=new java.util.Date();

我们也可以一劳永逸:

import java.util.Date;

class  Test2{
    public static void arr(){
        Date date=new Date();
    }
}

跳转过去看看:7ad40e8b7e354bda8d7fdb921a817cb0.png

a23f1d9cab5c4ea38ce28ec77074e718.png

这就是这个类里面的所有方法。  

69c04dcb11c9459b886152c709a21e04.png

现在,再让我们回到Date类,d248e3327d814cb78e142e3ca56dd307.png 56cc9257519047c7b9381e50f4e4591e.png

cb47f917432a4021ad93401eda7b76e8.png

 总之,这几个类都放在java.util这个包里。

这个时候,它们可以合并:

/*import java.util.Scanner;
import java.util.Arrays;
import java.util.Date;*/

import java.util.*;

这里的 * 被称作通配符,但不建议这样做,我们更建议显示的指定要导入的类名,否则还是容易出现冲突的情况。

比如这个:

bca44a08cbf24837bad57589120cb364.png

这个时候编译器就会报错,因为

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

中都存在Date这个类。还记得我们之前提过的包的好处吗?

怕你忘记了,我再写一遍: 

使用包的好处包括:

  • 防止类名冲突:每个包都有自己的命名空间,避免了类名的冲突。(同一个工程里允许存在相同名称的类,只要处在不同的包中即可)
  • 组织和管理类:将类组织成逻辑上相关的包,使得代码更易于维护和管理。
  • 访问控制:包可以用于控制类和成员的访问级别和可见性。

但是,此时这个好处造成了错误。这个时候你只能这么写才不会报错:

import java.util.*;
//import java.util.Date;这样也是不可以的
import java.sql.*;

class  Test2{
    public static void arr(){
        int [] array={1,2,3,4,5};
        System.out.println(Arrays.toString(array));
        java.util.Date date=new java.util.Date();
    }
}

静态导入:import static 导入包中的静态的方法和字段。

import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
    double x;
    double y;

    //静态导入的方法写起来更方便一些
    //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)是Java中用来引入其他类或接口的机制,类似于C语言中的#include,但是两者有一些区别:

导入的是类或接口:Java中的包导入是用来引入其他类或接口,而C语言中的#include可以引入其他文件中的任何内容,包括函数、变量等。在C语言里面是把头文件的代码都拷贝过来了,但是java的import只是为了写代码的时候不用一直带着类的限定名走,方便。

导入的是类的完整限定名:Java中的包导入是用来引入其他类或接口的完整限定名,如java.util.ArrayList,而不是引入文件名或文件路径。

包名和路径不一定一致:Java中的包名和对应的文件路径不一定一致,而C语言中的文件名和对应的文件路径一般是一致的。

语法不同:Java中的包导入语法是以import关键字开头,后面跟着要导入的类或接口的完整限定名,如import java.util.ArrayList;而C语言中的#include语法是以#include关键字开头,后面跟着要引入的文件名或文件路径,如#include <stdio.h>。

总的来说,Java中的包导入机制更加严谨和规范,可以避免命名冲突和代码重复。同时,Java中的包导入也可以提高代码的可读性和可维护性,使代码更加易于理解和修改。

自定义包

基本规则
在文件的最上方加上一个package语句指定该代码在哪个包中;

包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式

包名要和代码路径相匹配.例如创建combitdemo1的包,那么会存在一个对应的路径com/bit/demo1来存储代码.

如果一个类没有package语句,则该类被放到一个默认包(src)中.

fec3798be38e43bb8099bb6e0ed693fb.png

 注意,你自定义的包的名字必须全是英文小写!一般是公司的域名颠倒数。

9d8fb37d621c4f798492550f797d7e4e.png

 6d8d9a3b380b45fda8e761c432f42db5.png

5afaba6a03c64360b8ce06b4e2ecd641.png

现在我们可以看作是三个文件夹,里面都可以放java文件  

3b78d3511ef64b4a8c1347efa38342cd.png

 注意每一个文件的package语句:17801da2d98f4b67b9ac8afdf3ddf506.png

常见的包

1、java.lang系统常用基础类(String、Object),此包从JDK1.1后自动导入。

双击shift,在Classes中搜索Math,

5fe99f1e9e724f9ca6c2955fd21c8602.png

 43970082ae8341e38efa38d54ab65661.png

 跳转的java.lang,最下面的类就是它的类。当你想要导入这些类的时候,这些类都是不需要通过import去导入的:3379528f18584a8abd99d9cb25f47ed2.png

 但是如果你要导入的是java.lang的子包的类,例如reflect,你就需要写:import java.lang.reflect:

 930c88b25cee442fa192d8c76d6f0b33.png

2.java.lang.reflect;java 反射编程包
3.iava.net:进行网络编程开发包。
4.java.sql:进行数据库开发的支持包。
5.java.util:是java提供的工具程序包。(集合类等)非常重要
6.java.io:l/O编程开发包。

在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。


static修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。(static不可以修饰局部变量!属于类的变量)
 

【静态成员变量特性】

1.不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中,不依赖于对象

2.既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问

3.类变量存储在方法区当中

4.生命周期伴随类的一生(即: 随类的加载而创建,随类的卸载而销毁)

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

//...

    public Student(String name, String gender, int age, double score) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.score = score;
    }

    public static void main(String[] args) {
// 静态成员变量可以直接通过类名访问
        System.out.println(Student.classRoom);

        Student s1 = new Student("Li leilei","男",18, 3.8);
        Student s2 = new Student("Han MeiMei", "女", 19, 4.0);
        Student s3 = new Student("Jim", "男",18 ,2.6);

// 也可以通过对象访问: 但是classRoom是三个对象共享的
        System.out.println(s1.classRoom);
        System.out.println(s2.classRoom);
        System.out.println(s3.classRoom);

    }
}

84864539d7814cf9ab1df227834da9b9.png

static修饰成员方法

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

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 访问控制

那static属性应该如何访问呢?

Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的(值得注意的是,上面那个代码编译出错的原因是加了private修饰静态成员变量)。

public class Student{
//...
    private static String classRoom = "Bit306";
//...


public static String getClassRoom(){
    return classRoom;
    }
}
public class TestStudent {
public static void main(String[] args){
     Systemout.println(Student.getClassRoom());
}

【静态方法特性】

1.不属于某个具体的对象,是类方法

2.可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者

前者虽然也是合法的,但编译器还是会解析出原来的类。例如,使用对象来调用main()并不代表main会知道是哪个对象引用所做的调用。如此调用的方法也还是静态的!

3.不能在静态方法中访问任何非静态成员变量

静态的方法是在无关特定类的实例情况下执行的。甚至也不会有该类的实例出现。因为静态的方法是通过类的名称来调用,所以静态的方法无法引用到该类的任何实例变量。在此情况下,静态的方法也不会知道可以使用哪个实例变量值。

4.要专门写一个Setter和Getter。静态方法内部不能直接(但是可以创建出一个实例来调用)调用非静态成员方法,或者成员变量,即,static方法里面不可以使用this,无法传递this引用。

public static String getClassRoom(){
System.out.println(this);
return classRoom;
}
// 编译失败: Error:(35,28) java: 无法从静态上下文中引用非静态 变量 this


public int getAge() {
        return age;
}

public static String getClassRoom(){
age += 1;
return classRoom;
}
// 编译失败: Error:(35,9)java: 无法从静态上下文中引用非静态 变量 age

//但可以通过对象的引用调用,即再创建一个对象用来给非静态。
public static String getClassRoom(){
    Student student=new Student(); 
    int age=student.getAge();
    age += 1;
    return classRoom;
}

 5.静态方法无法重写,不能用来实现多态(后序多态位置详细讲解)。

public static String getClassRoom(){
doClass();
return classRoom;
}
// 编译报错: Error:(35,9)java: 无法从静态上下文中引用非静态 方法 doClass()

如果从静态方法调用非静态方法,但此非静态方法没有用到实例变量,这样会通过编译吗?
不会。编译器现在可以知道你有没有使用实例变量,你自己也知道。但如果现在可以通过,后来却把非静态变量改成会使用实例变量呢?又如果子类去覆盖这个方法成有用到实例变量的版本呢?所以不行。

static成员变量初始化

注意!静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性。

如果你没有给静态变量赋初值,它就会被设定默认值。int会被设定为0。静态变量的默认值会是该变量类型的默认值,就像实例变量所被赋予的默认值一样。

静态成员变量的初始化分为两种:就地初始化静态代码初始化

1、就地初始化:在定义时就直接给出初始值。

2、静态代码块初始化。

代码块

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

  • 普通代码块
  • 构造块(实例代码块)
  • 静态块
  • 同步代码块

普通代码块
普通代码块也称为局部代码块,定义在方法或语句中,用于限定变量的作用域,从而提高程序的可读性和安全性。例如: 

public class Example {
    public void method() {
        // 普通代码块
        {
            int a = 10;
            System.out.println("a = " + a);
        }
        // a 变量在此处已经失效
    }
}

构造代码块
构造代码块是定义在类中的代码块,用于在每次创建对象时执行一些操作,如给成员变量赋初始值。构造代码块会在每个构造方法执行之前执行。例如:

public class Example {
    int a;  // 成员变量
    {
        // 构造代码块,给成员变量 a 赋值为 10
        a = 10;
    }
    public Example() {
        // 构造方法
    }
}

静态代码块
静态代码块也定义在类中,用static关键字修饰,会在类加载时执行,只会执行一次。一般用于在类加载时初始化静态成员变量或进行其他一次性的操作。例如:

public class Example {
    static int a;  // 静态成员变量
    static {
        // 静态代码块,给静态成员变量 a 赋值为 10
        a = 10;
    }
}

同步代码块
同步代码块是一种用于多线程同步的机制,用synchronized关键字修饰,用于保证同一时间只能有一个线程进入该代码块。例如:

public class Example {
    public synchronized void method() {
        // 同步方法
    }
    public void method2() {
        // 同步代码块
        synchronized (this) {
            // 执行同步操作
        }
    }
}

以上是Java中常见的几种代码块。需要注意的是,构造代码块和静态代码块都是在类中定义的,因此可以访问类中的成员变量和方法;而普通代码块和同步代码块都是在方法中定义的,只能访问方法中的局部变量和参数。

执行顺序:静态代码块>实例代码块>构造方法 :

只要类被加载,静态代码块就会被执行;实例代码块一定是实例化对象的时候才会被执行。

静态代码块只执行一次!类加载的时候就为静态属性开辟内存空间。

class Student {
    //成员变量:定义在方法的外部,类的内部!

    //普通成员变量/实例成员变量
    private String name;//姓名
    private int age;//年龄

    //静态成员变量
    public static String classRoom;
//----------------------------------------------------------------------

    {
        this.name = "xiaoli";
        this.age = 19;
        System.out.println("实例代码块!");
    }

    static {
        classRoom = "109";
        System.out.println("静态代码块!");
    }


    public Student() {
        System.out.println("不带参数的构造方法");
    }

    public Student(String name) {
        this.name = name;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        Student stu1=new Student();
        System.out.println("================");
        Student stu2=new Student();
    }
}

47de9d329c8a405c962bb99061175f94.png

 如果都是静态的,看定义的先后次序依次执行,先定义,先执行。

class Student {
    //成员变量:定义在方法的外部,类的内部!

    //普通成员变量/实例成员变量
    private String name;//姓名
    private int age;//年龄

    //静态成员变量
    public static String classRoom;
//----------------------------------------------------------------------

    {
        this.name = "xiaoli";
        this.age = 19;
        System.out.println("实例代码块!");
    }

    static {
        classRoom = "109";
        System.out.println("静态代码块!2");
    }

    static {
        classRoom = "888";
        System.out.println("静态代码块!1");
    }

    public Student() {
        System.out.println("不带参数的构造方法");
    }

    public Student(String name) {
        this.name = name;
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        Student stu1=new Student();
        System.out.println("================");
        Student stu2=new Student();
    }
}

72945064487c4fcb9abd5039ee721d0b.png

 合并后,静态变量赋值只认后面那个。也就是说,最后,classRoom的值是888。

最后,非静态方法是可以调用该类静态的方法或静态的变量。

还有,同一类的方法可以直接访问该类的静态变量,不需要使用类名来访问。这是因为静态变量是类级别的变量,可以在整个类中共享访问。以下是一个简单的示例:

public class MyClass {
    public static int myStaticVariable = 42;

    public static void myStaticMethod() {
        System.out.println("The value of myStaticVariable is: " + myStaticVariable);
    }
}

在这个例子中,myStaticMethod()方法可以直接访问myStaticVariable静态变量,不需要使用类名来访问。

总结就是:所有方法,包括静态方法和实例方法,都可以直接访问该类的静态变量。同一个类里面可以直接用,但是类外要+类名. ,除非加了private不可以再用。

一些易错题练习:

阅读如下代码。 请问,对语句行 test.hello(). 描述正确的有()

package NowCoder;
class Test {
	public static void hello() {
	    System.out.println("hello");
	}
}
public class MyApplication {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test test=null;
		test.hello();
	}
}

A.能编译通过,并正确运行

B.因为使用了未初始化的变量,所以不能编译通过

C.以错误的方式访问了静态方法

D.能编译通过,但因变量为null,不能正常运行

故:答案选A

hello()方法是一个静态方法,调用静态方法不需要创建实例对象。

此时的 Test test=null; 表示test这个引用不指向任何对象。

静态方法不依赖于对象,所以它指不指向对象对静态方法来说都没有关系。

但是我们需要牢记,静态方法的正确访问方式应该是通过类型来访问。即:Test.hello();

如下哪些在main函数中使用是正确的()

public class Test{

  private float f=1.0f;

  int m=12;

  static int n=1;

  public static void main(String args[]){

    Test t=new Test();

  }

}

作业内容

A.t.f = 3.0

B.this.n

C.Test.m

D.Test.n

A:f是float类型,3.0默认是double,所以此时不能赋值

B:n是静态的,需要通过类名访问,不能通过this访问,this代表当前对象的引用,但是静态的成员变量不属于this。

C:m是实例成员变量,需要通过对象来进行调用。

D:正确

故:答案选D

给定以下代码:

class Test{	
	public String toString() {
		System.out.print("aaa");
		return "bbb";
	}
}

public static void main(String[] args) {
    Test test = new Test();
	System.out.println(test);
}

A.aaa

B.bbb

C.aaabbb

D.bbbaaa

当我们使用System.out.println()打印对象时,会默认调用Object类的toString()方法来获取打印内容。在该代码中,Test类重写了toString()方法,使其在返回"bbb"之前输出"aaa",所以在执行System.out.println(test)时会先输出"aaa",再输出"bbb"。

故:答案选C

import static 能够导入一些静态方法      (✔)

import static,它导入一个类中的静态成员,如静态方法、静态属性等,以便在代码中直接使用这些成员,而不需要通过类名来访问。例如:

import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

public class Test {
    public static void main(String[] args) {
        double radius = 2.0;
        double area = PI * radius * radius;
        double diagonal = sqrt(radius * radius + radius * radius);
    }
}

上述代码中,通过静态导入语法导入了 java.lang.Math 类中的静态成员 PI 和 sqrt,从而在 main 方法中可以直接使用这些成员,而不需要通过 Math.PI 或 Math.sqrt 来访问。

import 语句能够导入一个指定的包    (×)

导入的是包下的所有类,用到这个包底下哪个类就回调用哪个。

import java.util;     //错误的
import java.util.*;
import java.util.Arrays;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值