当前位置: 首页 > news >正文

java基础(day11)

目录

一.Static关键字

1. static 静态成员变量

2. static 静态方法

3. 接口中的常量和方法

4. static静态代码块

        4.1. 静态代码块的声明

        4.2. 静态代码块的作用

        4.3. 执行顺序

 5. 小结

二.Package包

1. package关键字

2. 包作用域

3. import关键字

5. 小结

三.作用域

1. public公共访问修饰符

2. private私有访问修饰符

3. protected保护访问修饰符

 4. friendly

5. 访问修饰符总结

 内部类

1. Inner Class 内部类

2. Anonymous Class 匿名类

3. Static Nested Class静态内部类

4. 小结


一.Static关键字

1. static 静态成员变量

        一个class类中定义的成员变量,我们称之为实例成员变量(成员变量)。实例成员变量的特点是,每个实例都有独立的成员变量,各个实例的同名成员变量互不影响。除此以外,还有一种成员变量,是使用static关键字修饰的成员变量,称为静态成员变量:static field

实例成员变量在每个实例中都有自己的一个独立“空间”,但是静态成员变量只有一个共享“空间”,所有实例都会共享该成员变量。举个例子:

class Person {public String name;public int age;// 定义静态成员变量number:public static int number;public Person(String name, int age) {this.name = name;this.age = age;}
}

public class Main {public static void main(String[] args) {Person ming = new Person("Xiao Ming", 12);Person hong = new Person("Xiao Hong", 15);ming.number = 88;System.out.println(hong.number);hong.number = 99;System.out.println(ming.number);}
}

注:对于静态成员变量,无论修改哪个实例的静态成员变量,都是访问相同的内存空间:所有实例的静员态成员变量都被修改了,原因是静态成员变量并不属于实例。

内存图:

        ┌──────────────────┐
ming ──>│Person instance   │├──────────────────┤│name = "Xiao Ming"││age = 12          ││number ───────────┼──┐    ┌─────────────┐└──────────────────┘  │    │Person class ││    ├─────────────┤├───>│number = 99  │┌──────────────────┐  │    └─────────────┘
hong ──>│Person instance   │  │├──────────────────┤  ││name = "Xiao Hong"│  ││age = 15          │  ││number ───────────┼──┘└──────────────────┘

推荐用类名.静态成员变量来访问静态成员变量。可以把静态成员变量理解为描述class本身的成员变量(非实例成员变量)。

不推荐用实例变量.静态成员变量去访问静态成员变量。因为在Java程序中,实例对象并没有静态成员变量。在代码中,实例对象能访问静态成员变量只是因为编译器可以根据实例类型自动转换为类名.静态成员变量来访问静态对象。

2. static 静态方法

        用static修饰的方法称为静态方法。调用实例方法必须通过一个实例对象,而调用静态方法则不需要实例对象,通过类名就可以调用。

public class Main {public static void main(String[] args) {// 通过类名调用静态方法Person.setNumber(99);// 通过类名调用静态成员变量System.out.println(Person.number);}
}class Person {// 静态成员变量public static int number;// 静态方法public static void setNumber(int value) {number = value;}
}

态方法在定义或使用时,应该注意:

  • 静态方法属于class类级别的,而不属于实例对象。因此,静态方法内部,无法访问this变量,也无法访问实例对象的成员变量;
  • 静态方法只能访问静态成员变量静态方法
  • 普通方法内部,也可以访问静态成员变量静态方法
  • 通过实例变量也可以调用静态方法静态成员变量(这只是编译器自动帮我们把实例改写成类名而已),不推荐使用。所以,通常情况下,通过实例变量访问静态成员变量和静态方法,会得到一个编译警告;
  • 静态方法经常用于工具类。例如:
    • Arrays.sort()
    • Math.random()
  • 静态方法也经常用于辅助方法。注意到Java程序的入口main()也是静态方法。

3. 接口中的常量和方法

        因为interface是一个特殊的“抽象类”,所以它不能定义实例成员变量。但是,interface是可以有“静态成员变量”的,并且静态成员变量默认为final,所以这个“静态成员变量”其实是一个常量:

public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

实际上,因为interface的成员变量只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:编译器会自动把该成员变量变为public static final类型。

        interface接口中也可以有静态方法,只能使用接口名称直接调用。例如:

interface AutoSearch{
    
    static void quickSearch(){
        //....
    }
}

public class Main {
    public static void main(String[] args) {
        // 调用接口的静态方法
        AutoSearch.quickSearch();
    }
}

注:

接口中的默认方法,必须使用“接口实现类”的对象,才能调用。

接口中的静态方法,必须通过“接口名.方法名()”才能进行调用。

4. static静态代码块

        4.1. 静态代码块的声明

Java类中使用static关键字和{}声明的静态代码块,静态代码块在“类被加载”的时候运行,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。

        4.2. 静态代码块的作用

如果有些代码逻辑或对象,需要在项目启动的时候就执行或创建,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件或者数据库连接池对象等,我们就可以都放入静态代码块中。

另外,静态代码块中,只允许调用静态方法或静态的成员变量,不允许调用普通方法或普通成员变量。

        4.3. 执行顺序

静态代码块 > 构造代码块 > 构造函数 > 普通代码块

public class CodeBlock {static{System.out.println("静态代码块A");}{System.out.println("构造代码块1");}{System.out.println("构造代码块2");}static{System.out.println("静态代码块B");}public CodeBlock(){System.out.println("无参构造函数");}public void sayHello(){{System.out.println("普通代码块");}}public static void main(String[] args) {System.out.println("执行了main方法");new CodeBlock().sayHello();;System.out.println("---------------");new CodeBlock().sayHello();;}
}

如果存在继承关系,基本的执行流程是“先静态(父类 > 子类),再非静态(父类 > 子类)”的顺序关系。

public class Demo {public static void main(String[] args) {Son s1 = new Son();System.out.println();Son s2 = new Son();}
}// 父类
class Base{static {System.out.println("Base父类的静态代码块");}{System.out.println("Base父类的构造代码块");}public Base() {System.out.println("Base父类的无参构造方法");}public Base(int val) {System.out.println("Base父类的有参构造方法");}
}// 子类
class Son extends Base{static {System.out.println("Son子类的静态代码块");}{System.out.println("Son子类的构造代码块");}public Son() {System.out.println("Son子类的无参构造方法");}
}
 5. 小结
  • 使用static关键字修饰的成员变量,被称为静态成员变量,它属于所有实例“共享”的成员变量。
  • 使用static修饰的方法,被称为静态方法。
  • 静态方法或静态成员变量,建议通过类名进行访问。(不推荐使用实例对象访问)
  • 静态方法只能可以调用静态的方法或成员变量,不能调用普通的方法或成员变量。静态方法无法访问this
  • 使用static修饰的代码块,被称为静态代码块。在静态代码块在“类被加载”的时候运行,而且只运行一次。
  • 静态代码块中,只允许调用静态方法或静态的成员变量,不允许调用普通方法或普通成员变量。
  • 执行流程:静态代码块 > 构造代码块 > 构造函数 > 普通代码块。
  • 如果存在继承关系,则执行流程为:先“静态”(父类 > 子类),再“非静态”(父类 > 子类)
  • 静态方法常用于工具类和辅助方法。

二.Package包

1. package关键字

        在Java中,我们使用package来解决名字冲突。Java定义了一种使用名字来命名空间的方式,称之为package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是 包名.类名。

在定义class的时候,我们需要在第一行声明这个class属于哪个包。

  • 小明的Person类存放在包ming下面,因此,完整类名是ming.Person
  • package ming; // 声明包名mingpublic class Person {
    }

    例如:

JDK的Arrays类存放在包java.util下面,因此,完整类名是java.util.Arrays

        在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。包可以是多层结构,用.隔开。例如:java.util。没有定义包名的class,它使用的是默认包(default package),非常容易引起名字冲突,因此,不推荐使用默认包(default package)。

        包没有父子关系。java.utiljava.util.zip是不同的包,两者没有任何继承关系。

2. 包作用域

        位于同一个包的类,可以访问包作用域的字段和方法。不用publicprotectedprivate修饰的字段和方法就是包作用域。例如,Person类定义在hello包下面:

package hello;public class Person {// 包作用域:void hello() {System.out.println("Hello!");}
}
3. import关键字
// Person.java
package ming;// 导入完整类名:
import mr.jun.Arrays;public class Person {public void run() {Arrays arrays = new Arrays();}
}第二种
// Person.java
package ming;public class Person {public void run() {mr.jun.Arrays arrays = new mr.jun.Arrays();}
}

 还有一种写法是使用import static的语法,它可以导入一个类的静态字段和静态方法:

package main;// 导入System类的所有静态字段和静态方法:
import static java.lang.System.*;public class Main {public static void main(String[] args) {// 相当于调用System.out.println(…)out.println("Hello, world!");}
}

Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:

  • 如果是完整类名,就直接根据完整类名查找这个class
  • 如果是简单类名,按下面的顺序依次查找:
    • 查找当前package是否存在这个class
    • 查找import的包是否包含这个class

查找java.lang包是否包含这个class

5. 小结
  • java内建的package机制是为了避免class命名冲突。
  • JDK的核心类使用java.lang包,编译器会自动导入。
  • JDK的其它常用类定义在java.util.*java.math.*java.text.*……
  • 包名推荐使用倒置的域名,并且由小写字母组成。例如org.apache

三.作用域

1. public公共访问修饰符

        定义为publicclassinterface可以被其他任何类访问:

package abc;

public class Hello {
    public void hi() {
    }
}

package xyz;class Main {void foo() {// Main可以访问HelloHello h = new Hello();}
}

         定义为publicfieldmethod可以被其他类访问,前提是首先有访问class的权限:

package abc;public class Hello {public void hi() {}
}

上面的hi()方法是public,可以被其他类调用,前提是首先要能访问Hello类:

package xyz;class Main {void foo() {Hello h = new Hello();h.hi();}
}

 一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

2. private私有访问修饰符

定义为private的field、method无法被其他类访问:

package abc;public class Hello {// 不能被其他类调用:private void hi() {}public void hello() {this.hi();}
}

实际上,确切地说,private访问权限被限定在class的内部,而且与方法声明顺序无关。推荐把private方法放到后面,因为public方法定义了类对外提供的功能,阅读代码的时候,应该先关注public方法

3. protected保护访问修饰符

        protected作用于继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类:

package abc;public class Hello {// protected方法:protected void hi() {}
}

上面的protected方法可以被继承的类访问:

package xyz;class Main extends Hello {void foo() {// 可以访问protected方法:hi();}
}
 4. friendly

   friendly默认访问修饰符,也代表包作用域,是指一个类允许访问同一个package的没有publicprivate修饰的class,以及没有publicprotectedprivate修饰的字段和方法。

package abc;
// friendly权限的类:
class Hello {// friendly权限的方法:void hi() {}
}

 

只要在同一个包,就可以访问friendly权限的classfieldmethod。注意,包名必须完全一致,包没有父子关系,com.apachecom.apache.abc是不同的包。

package abc;

class Main {
    void foo() {
        // 可以访问package权限的类:
        Hello h = new Hello();
        // 可以调用package权限的方法:
        h.hi();
    }
}

5. 访问修饰符总结
  • friendly(默认访问修饰符): 在同一包内可见,不使用任何修饰符。使用位置:类、接口、变量、方法。
  • private(私有访问修饰符) : 在同一类的内部可见。使用位置:变量、方法。
  • public (公共访问修饰符): 对所有类可见。使用位置:类、接口、变量、方法
  • protected(保护访问修饰符) : 对同一包内的类、或子类(子类可以不同包)可见。使用位置:变量、方法。

修饰符

当前类

同一包内

子类(同一包)

子类(不同包)

其他包

public

Yes

Yes

Yes

Yes

Yes

protected

Yes

Yes

Yes

Yes

No

friendly

Yes

Yes

Yes

No

No

private

Yes

No

No

No

No

 内部类

        在Java程序中,通常情况下,我们把不同的类组织在不同的包下面,对于一个包下面的类来说,它们是在同一层次,没有父子关系:

java.lang
├── Math
├── Runnable
├── String
└── ...
1. Inner Class 内部类

        如果一个类定义在另一个类的内部,这个类就是Inner Class:

class Outer {class Inner {// 定义了一个Inner Class}
}

        上述定义的Outer是一个普通类,而Inner是一个Inner Class,它与普通类最大的区别:Inner Class的对象实例不能单独存在,必须依附于一个Outer Class外部类的对象实例。示例代码如下:

public class Main {public static void main(String[] args) {Outer outer = new Outer("Nested"); // 实例化一个OuterOuter.Inner inner = outer.new Inner(); // 实例化一个Innerinner.hello();}
}class Outer {private String name;Outer(String name) {this.name = name;}class Inner {void hello() {System.out.println("Hello, " + Outer.this.name);}}
}

这是因为Inner Class除了有一个this指向它自己,还隐含地持有一个Outer Class实例,可以用Outer.this访问这个实例。所以,实例化一个Inner Class不能脱离Outer实例。

Inner Class和普通Class相比,除了能引用Outer实例外,还有一个额外的“特权”,就是可以修改Outer Classprivate字段,因为Inner Class的作用域在Outer Class内部,所以能访问Outer Classprivate字段和方法。

// 外部类
public class Outer {// 成员变量private int value = 10;// 普通方法private void test() {System.out.println("外部类的test()");}// 静态代码块static {System.out.println("静态代码块:外部类Outer被加载");}// 内部类class Inner{private int value = 5;// 错误:因为内部类中不能定义静态代码块
//		static {
//			System.out.println("内部类Element被加载");
//		}public void build() {System.out.println("内部类Element的build()方法被执行");// 通过“this”关键字调用内部类自己的成员变量System.out.println("内部类Element的成员变量value=" + this.value);// 通过“外部类.this”调用外部类对象的成员变量System.out.println("外部类Demo的成员变量value=" + Outer.this.value);// 调用所在外部类对象的方法test();}}
}

观察Java编译器编译后的.class文件可以发现,Outer类被编译为Outer.class,而Inner类被编译为Outer$Inner.class

2. Anonymous Class 匿名类

        还有一种定义Inner Class的方法,它不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。示例代码如下:

public class Main {public static void main(String[] args) {Outer outer = new Outer("Nested");outer.asyncHello();}
}class Outer {private String name;Outer(String name) {this.name = name;}void asyncHello() {Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("Hello, " + Outer.this.name);}};new Thread(r).start();}
}

        观察asyncHello()方法,我们在方法内部实例化了一个RunnableRunnable本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable接口的匿名类,并且通过new实例化该匿名类,然后转型为Runnable。在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:

Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};

匿名类和Inner Class一样,可以访问Outer Classprivate字段和方法。之所以我们要定义匿名类,是因为在这里我们通常不关心这个类的定义,并且该类仅在定义的位置一次性使用,比直接定义Inner Class可以少写很多代码。

3. Static Nested Class静态内部类

        最后一种内部类和Inner Class类似,但是使用static修饰,称为静态内部类(Static Nested Class

package test;
// 普通类
// public class Outer{
// [静态]成员常量 [静态]成员常量 构造方法 静态代码块 [静态]方法
// 静态内部类
// class Inner
public class Outer2 {private double pi = 3.14;private static String name = "张三";public Outer2(){System.out.println("外部类的无参构造方法");}static {System.out.println("这个是外部类的静态代码块");}public static void test(){System.out.println("这个是外部的test");}public void eat(){System.out.println("外部类的普通方法");}@Overridepublic String toString(){return "Outer{"+"pi="+pi+",name="+name+'\''+ "}";}static class Inner2{private double e = 2.7;private static String names = "张三";public Inner2(){System.out.println("内部类的无参构造方法");}public void test(){// 静态内部类不能访问外部类的非静态的成员变量,也不能出现Outer2.this// Outer2.this.pi = 3.1415;System.out.println("修改前:"+Outer2.name);Outer2.name = "李四";System.out.println("修改后:"+Outer2.name);// 静态内部类不能访问外部类的非静态的成员方法// System.out.pringln(Outer2.this.eat());// 调用外部类的成员方法// 访问外部类的静态test方法// Outer2.this.test();// 不能用,不能有thisOuter2.test();// 类名.方法名()System.out.println(this.toString());// 调用本类的toStringdosth();// 调用自己的静态方法}// 静态内部类中允许存在静态的方法public static void dosth(){System.out.println("内部类的静态的dosth");}// 内部类的toString@Overridepublic String toString(){return "Inner{"+"e="+e+'}';}}}

        用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outerprivate静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。

4. 小结
  • Java的内部类可分为:Inner Class(内部类)、Anonymous Class(匿名类)和 Static Nested Class(静态内部类)三种。
  • Inner Class Anonymous Class本质上是相同的,都必须依附于Outer Class的实例,即隐含地持有Outer.this实例,并拥有Outer Classprivate访问权限。
  • Static Nested Class 是独立类,但拥有 Outer Class private访问权限。
http://www.dtcms.com/a/288300.html

相关文章:

  • 突破 MySQL 性能瓶颈:死锁分析 + 慢查询诊断 + 海量数据比对实战
  • Redis布隆过滤器的学习(六)
  • 财务数字化——解读财务指标及财务分析的基本步骤与方法【附全文阅读】
  • 基于LSTM的时间序列到时间序列的回归模拟
  • 06-人机共生:Prompt之外的思考
  • Linux Shell 命令 + 项目场景
  • windows11下基于docker单机部署ceph集群
  • 同步队列阻塞器AQS的执行流程,案例图
  • 张量交换维度(转置),其实是交换了元素的排列顺序
  • lvs集群技术(Linux virual server)
  • MinIO深度解析:从核心特性到Spring Boot实战集成
  • 笔试大题20分值(用两个栈实现队列)
  • 基于densenet网络创新的肺癌识别研究
  • lvs 集群技术
  • 渗透高级----第四章:XSS进阶
  • 如何优雅调整Doris key顺序
  • linux--------------------BlockQueue的生产者消费模型
  • 【Docker基础】深入解析Docker-compose核心配置:Services服务配置详解
  • Gitee 提交信息的规范
  • 算法基础知识总结
  • GoC 图片指令
  • BeanFactory 和 FactoryBean 的区别
  • 架构探索笔记【1】
  • 如何快速学习一门新技术
  • 实用的文件和文件夹批量重命名工具
  • 手撕Spring底层系列之:注解驱动的魔力与实现内幕
  • 【Linux】重生之从零开始学习运维之Nginx
  • 【服务器与部署 14】消息队列部署:RabbitMQ、Kafka生产环境搭建指南
  • Linux中添加重定向(Redirection)功能到minishell
  • 中小机构如何低成本搭建教育培训平台?源码开发+私有化部署攻略