首页 > 编程语言 >Java内部类详解

Java内部类详解

时间:2022-12-20 16:35:09浏览次数:44  
标签:部类 Java 内部 public 详解 Inner Outter class


说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉。原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法。今天我们就来一探究竟。下面是本文的目录大纲:

  一.内部类基础

  二.深入理解内部类

  三.内部类的使用场景和好处

  四.常见的与内部类相关的笔试面试题

  若有不正之处,请多谅解并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

  javascript:void(0)p/3811445.html

一.内部类基础

  在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。

  1.成员内部类

  成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:




​class​​  ​​Circle {​



​double​​  ​​radius = ​​ ​​0​​ ​​;​



 



​public​​  ​​Circle(​​ ​​double​​  ​​radius) {​



​this​​ ​​.radius = radius;​



​}​



 



​class​​  ​​Draw {     ​​ ​​//内部类​



​public​​  ​​void​​  ​​drawSahpe() {​



​System.out.println(​​ ​​"drawshape"​​ ​​);​



​}​



​}​



​}​




  这样看起来,类Draw像是类Circle的一个成员,Circle称为外部类。成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。




​class​​  ​​Circle {​



​private​​  ​​double​​  ​​radius = ​​ ​​0​​ ​​;​



​public​​  ​​static​​  ​​int​​  ​​count =​​ ​​1​​ ​​;​



​public​​  ​​Circle(​​ ​​double​​  ​​radius) {​



​this​​ ​​.radius = radius;​



​}​



 



​class​​  ​​Draw {     ​​ ​​//内部类​



​public​​  ​​void​​  ​​drawSahpe() {​



​System.out.println(radius);  ​​ ​​//外部类的private成员​



​System.out.println(count);   ​​ ​​//外部类的静态成员​



​}​



​}​



​}​




  不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:




​外部类.​​ ​​this​​ ​​.成员变量​



​外部类.​​ ​​this​​ ​​.成员方法​




  虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:




​class​​  ​​Circle {​



​private​​  ​​double​​  ​​radius = ​​ ​​0​​ ​​;​



 



​public​​  ​​Circle(​​ ​​double​​  ​​radius) {​



​this​​ ​​.radius = radius;​



​getDrawInstance().drawSahpe();   ​​ ​​//必须先创建成员内部类的对象,再进行访问​



​}​



 



​private​​  ​​Draw getDrawInstance() {​



​return​​  ​​new​​  ​​Draw();​



​}​



 



​class​​  ​​Draw {     ​​ ​​//内部类​



​public​​  ​​void​​  ​​drawSahpe() {​



​System.out.println(radius);  ​​ ​​//外部类的private成员​



​}​



​}​



​}​




  成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:




​public​​  ​​class​​  ​​Test {​



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



​//第一种方式:​



​Outter outter = ​​ ​​new​​  ​​Outter();​



​Outter.Inner inner = outter.​​ ​​new​​  ​​Inner();  ​​ ​​//必须通过Outter对象来创建​



 



​//第二种方式:​



​Outter.Inner inner1 = outter.getInnerInstance();​



​}​



​}​



 



​class​​  ​​Outter {​



​private​​  ​​Inner inner = ​​ ​​null​​ ​​;​



​public​​  ​​Outter() {​



 



​}​



 



​public​​  ​​Inner getInnerInstance() {​



​if​​ ​​(inner == ​​ ​​null​​ ​​)​



​inner = ​​ ​​new​​  ​​Inner();​



​return​​  ​​inner;​



​}​



 



​class​​  ​​Inner {​



​public​​  ​​Inner() {​



 



​}​



​}​



​}​




  内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

  2.局部内部类

  局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。




​class​​  ​​People{​



​public​​  ​​People() {​



 



​}​



​}​



 



​class​​  ​​Man{​



​public​​  ​​Man(){​



 



​}​



 



​public​​  ​​People getWoman(){​



​class​​  ​​Woman ​​ ​​extends​​  ​​People{   ​​ ​​//局部内部类​



​int​​  ​​age =​​ ​​0​​ ​​;​



​}​



​return​​  ​​new​​  ​​Woman();​



​}​



​}​




  注意,局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。

  3.匿名内部类

  匿名内部类应该是平时我们编写代码时用得最多的,在编写事件监听的代码时使用匿名内部类不但方便,而且使代码更加容易维护。下面这段代码是一段Android事件监听代码:




​scan_bt.setOnClickListener(​​ ​​new​​  ​​OnClickListener() {​



 



​@Override​



​public​​  ​​void​​  ​​onClick(View v) {​



​// TODO Auto-generated method stub​



 



​}​



​});​



 



​history_bt.setOnClickListener(​​ ​​new​​  ​​OnClickListener() {​



 



​@Override​



​public​​  ​​void​​  ​​onClick(View v) {​



​// TODO Auto-generated method stub​



 



​}​



​});​




  这段代码为两个按钮设置监听器,这里面就使用了匿名内部类。这段代码中的:




​new​​  ​​OnClickListener() {​



 



​@Override​



​public​​  ​​void​​  ​​onClick(View v) {​



​// TODO Auto-generated method stub​



 



​}​



​}​




  就是匿名内部类的使用。代码中需要给按钮设置监听器对象,使用匿名内部类能够在实现父类或者接口中的方法情况下同时产生一个相应的对象,但是前提是这个父类或者接口必须先存在才能这样使用。当然像下面这种写法也是可以的,跟上面使用匿名内部类达到效果相同。



1



2



3



4



5



6



7



8



9



10



11



12



13



14



15



16



17



18



19



20



21


​private​​  ​​void​​  ​​setListener()​



​{​



​scan_bt.setOnClickListener(​​ ​​new​​  ​​Listener1());       ​



​history_bt.setOnClickListener(​​ ​​new​​  ​​Listener2());​



​}​



 



​class​​  ​​Listener1 ​​ ​​implements​​  ​​View.OnClickListener{​



​@Override​



​public​​  ​​void​​  ​​onClick(View v) {​



​// TODO Auto-generated method stub​



 



​}​



​}​



 



​class​​  ​​Listener2 ​​ ​​implements​​  ​​View.OnClickListener{​



​@Override​



​public​​  ​​void​​  ​​onClick(View v) {​



​// TODO Auto-generated method stub​



 



​}​



​}​




  这种写法虽然能达到一样的效果,但是既冗长又难以维护,所以一般使用匿名内部类的方法来编写事件监听代码。同样的,匿名内部类也是不能有访问修饰符和static修饰符的。

  匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

  4.静态内部类

  静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。




​public​​  ​​class​​  ​​Test {​



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



​Outter.Inner inner = ​​ ​​new​​  ​​Outter.Inner();​



​}​



​}​



 



​class​​  ​​Outter {​



​public​​  ​​Outter() {​



 



​}​



 



​static​​  ​​class​​  ​​Inner {​



​public​​  ​​Inner() {​



 



​}​



​}​



​}​



  

Java内部类详解_成员内部类

二.深入理解内部类

  1.为什么成员内部类可以无条件访问外部类的成员?

  在此之前,我们已经讨论过了成员内部类可以无条件访问外部类的成员,那具体究竟是如何实现的呢?下面通过反编译字节码文件看看究竟。事实上,编译器在进行编译的时候,会将成员内部类单独编译成一个字节码文件,下面是Outter.java的代码:




​public​​  ​​class​​  ​​Outter {​



​private​​  ​​Inner inner = ​​ ​​null​​ ​​;​



​public​​  ​​Outter() {​



 



​}​



 



​public​​  ​​Inner getInnerInstance() {​



​if​​ ​​(inner == ​​ ​​null​​ ​​)​



​inner = ​​ ​​new​​  ​​Inner();​



​return​​  ​​inner;​



​}​



 



​protected​​  ​​class​​  ​​Inner {​



​public​​  ​​Inner() {​



 



​}​



​}​



​}​




  编译之后,出现了两个字节码文件:

Java内部类详解_外部类_02

  反编译Outter$Inner.class文件得到下面信息:




​E:\Workspace\Test\bin\com\cxh\test2>javap -v Outter$Inner​



​Compiled from ​​ ​​"Outter.java"​



​public​​  ​​class​​  ​​com.cxh.test2.Outter$Inner ​​ ​​extends​​  ​​java.lang.Object​



​SourceFile: ​​ ​​"Outter.java"​



​InnerClass:​



​#​​ ​​24​​ ​​= #​​ ​​1​​  ​​of #​​ ​​22​​ ​​; ​​ ​​//Inner=class com/cxh/test2/Outter$Inner of class com/cxh/tes​



​t2/Outter​



​minor version: ​​ ​​0​



​major version: ​​ ​​50​



​Constant pool:​



​const​​  ​​#​​ ​​1​​  ​​= ​​ ​​class​​         ​​#​​ ​​2​​ ​​;     ​​ ​​//  com/cxh/test2/Outter$Inner​



​const​​  ​​#​​ ​​2​​  ​​= Asciz        com/cxh/test2/Outter$Inner;​



​const​​  ​​#​​ ​​3​​  ​​= ​​ ​​class​​         ​​#​​ ​​4​​ ​​;     ​​ ​​//  java/lang/Object​



​const​​  ​​#​​ ​​4​​  ​​= Asciz        java/lang/Object;​



​const​​  ​​#​​ ​​5​​  ​​= Asciz        ​​ ​​this​​ ​​$​​ ​​0​​ ​​;​



​const​​  ​​#​​ ​​6​​  ​​= Asciz        Lcom/cxh/test2/Outter;;​



​const​​  ​​#​​ ​​7​​  ​​= Asciz        <init>;​



​const​​  ​​#​​ ​​8​​  ​​= Asciz        (Lcom/cxh/test2/Outter;)V;​



​const​​  ​​#​​ ​​9​​  ​​= Asciz        Code;​



​const​​  ​​#​​ ​​10​​  ​​= Field       #​​ ​​1​​ ​​.#​​ ​​11​​ ​​; ​​ ​​//  com/cxh/test2/Outter$Inner.this$0:Lcom/cxh/t​



​est2/Outter;​



​const​​  ​​#​​ ​​11​​  ​​= NameAndType #​​ ​​5​​ ​​:#​​ ​​6​​ ​​;​​ ​​//  this$0:Lcom/cxh/test2/Outter;​



​const​​  ​​#​​ ​​12​​  ​​= Method      #​​ ​​3​​ ​​.#​​ ​​13​​ ​​; ​​ ​​//  java/lang/Object."<init>":()V​



​const​​  ​​#​​ ​​13​​  ​​= NameAndType #​​ ​​7​​ ​​:#​​ ​​14​​ ​​;​​ ​​//  "<init>":()V​



​const​​  ​​#​​ ​​14​​  ​​= Asciz       ()V;​



​const​​  ​​#​​ ​​15​​  ​​= Asciz       LineNumberTable;​



​const​​  ​​#​​ ​​16​​  ​​= Asciz       LocalVariableTable;​



​const​​  ​​#​​ ​​17​​  ​​= Asciz       ​​ ​​this​​ ​​;​



​const​​  ​​#​​ ​​18​​  ​​= Asciz       Lcom/cxh/test2/Outter$Inner;;​



​const​​  ​​#​​ ​​19​​  ​​= Asciz       SourceFile;​



​const​​  ​​#​​ ​​20​​  ​​= Asciz       Outter.java;​



​const​​  ​​#​​ ​​21​​  ​​= Asciz       InnerClasses;​



​const​​  ​​#​​ ​​22​​  ​​= ​​ ​​class​​        ​​#​​ ​​23​​ ​​;    ​​ ​​//  com/cxh/test2/Outter​



​const​​  ​​#​​ ​​23​​  ​​= Asciz       com/cxh/test2/Outter;​



​const​​  ​​#​​ ​​24​​  ​​= Asciz       Inner;​



 



​{​



​final​​  ​​com.cxh.test2.Outter ​​ ​​this​​ ​​$​​ ​​0​​ ​​;​



 



​public​​  ​​com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);​



​Code:​



​Stack=​​ ​​2​​ ​​, Locals=​​ ​​2​​ ​​, Args_size=​​ ​​2​



​0​​ ​​:   aload_0​



​1​​ ​​:   aload_1​



​2​​ ​​:   putfield        #​​ ​​10​​ ​​; ​​ ​​//Field this$0:Lcom/cxh/test2/Outter;​



​5​​ ​​:   aload_0​



​6​​ ​​:   invokespecial   #​​ ​​12​​ ​​; ​​ ​​//Method java/lang/Object."<init>":()V​



​9​​ ​​:   ​​ ​​return​



​LineNumberTable:​



​line ​​ ​​16​​ ​​: ​​ ​​0​



​line ​​ ​​18​​ ​​: ​​ ​​9​



 



​LocalVariableTable:​



​Start  Length  Slot  Name   Signature​



​0​​       ​​10​​       ​​0​​     ​​this​​        ​​Lcom/cxh/test2/Outter$Inner;​



 



 



​}​




  第11行到35行是常量池的内容,下面逐一第38行的内容:



final com.cxh.test2.Outter this$0;



  这行是一个指向外部类对象的指针,看到这里想必大家豁然开朗了。也就是说编译器会默认为成员内部类添加了一个指向外部类对象的引用,那么这个引用是如何赋初值的呢?下面接着看内部类的构造器:



public com.cxh.test2.Outter$Inner(com.cxh.test2.Outter);



  从这里可以看出,虽然我们在定义的内部类的构造器是无参构造器,编译器还是会默认添加一个参数,该参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,则无法对Outter this&0引用进行初始化赋值,也就无法创建成员内部类的对象了。

  2.为什么局部内部类和匿名内部类只能访问局部final变量?

  想必这个问题也曾经困扰过很多人,在讨论这个问题之前,先看下面这段代码:



1



2



3



4



5



6



7



8



9



10



11



12



13



14



15


​public​​  ​​class​​  ​​Test {​



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



 



​}​



 



​public​​  ​​void​​  ​​test(​​ ​​final​​  ​​int​​  ​​b) {​



​final​​  ​​int​​  ​​a = ​​ ​​10​​ ​​;​



​new​​  ​​Thread(){​



​public​​  ​​void​​  ​​run() {​



​System.out.println(a);​



​System.out.println(b);​



​};​



​}.start();​



​}​



​}​




1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outter1.class。默认情况下,编译器会为匿名内部类和局部内部类起名为Outterx.class(x为正整数)。

  

Java内部类详解_内部类_03

  根据上图可知,test方法中的匿名内部类的名字被起为 Test$1。

  上段代码中,如果把变量a和b前面的任一个final去掉,这段代码都编译不过。我们先考虑这样一个问题:

复制

Java内部类详解_外部类_04

  我们看到在run方法中有一条指令:



bipush 10



  这条指令表示将操作数10压栈,表示使用的是一个本地局部变量。这个过程是在编译期间由编译器默认进行,如果这个变量的值在编译期间可以确定,则编译器默认会在匿名内部类(局部内部类)的常量池中添加一个内容相等的字面量或直接将相应的字节码嵌入到执行字节码中。这样一来,匿名内部类使用的变量是另一个局部变量,只不过值和方法中局部变量的值相等,因此和方法中的局部变量完全独立开。

  下面再看一个例子:



1



2



3



4



5



6



7



8



9



10



11



12



13


​public​​  ​​class​​  ​​Test {​



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



 



​}​



 



​public​​  ​​void​​  ​​test(​​ ​​final​​  ​​int​​  ​​a) {​



​new​​  ​​Thread(){​



​public​​  ​​void​​  ​​run() {​



​System.out.println(a);​



​};​



​}.start();​



​}​



​}​




  反编译得到:

Java内部类详解_成员内部类_05

  我们看到匿名内部类Test$1的构造器含有两个参数,一个是指向外部类对象的引用,一个是int型变量,很显然,这里是将变量test方法中的形参a以参数的形式传进来对匿名内部类中的拷贝(变量a的拷贝)进行赋值初始化。

也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。

  从上面可以看出,在run方法中访问的变量a根本就不是test方法中的局部变量a。这样一来就解决了前面所说的 生命周期不一致的问题。但是新的问题又来了,既然在run方法中访问的变量a和test方法中的变量a不是同一个变量,当在run方法中改变变量a的值的话,会出现什么情况?

对,会造成数据不一致性,这样就达不到原本的意图和要求。为了解决这个问题,java编译器就限定必须将变量a限制为final变量,不允许对变量a进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。

  到这里,想必大家应该清楚为何 方法中的局部变量和形参都必须用final进行限定了。

  3.静态内部类有特殊的地方吗?

  从前面可以知道,静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,这个读者可以自己尝试反编译class文件看一下就知道了,是没有Outter this&0引用的。

三.内部类的使用场景和好处

  为什么在Java中需要内部类?总结一下主要有以下四点:

  1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

  2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

  3.方便编写事件驱动程序

  4.方便编写线程代码

  个人觉得第一点是最重要的原因之一,内部类的存在使得Java的多继承机制变得更加完善。

四.常见的与内部类相关的笔试面试题

根据注释填写(1),(2),(3)处的代码




​public​​  ​​class​​  ​​Test{​



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



​// 初始化Bean1​



​(​​ ​​1​​ ​​)​



​bean1.I++;​



​// 初始化Bean2​



​(​​ ​​2​​ ​​)​



​bean2.J++;​



​//初始化Bean3​



​(​​ ​​3​​ ​​)​



​bean3.k++;​



​}​



​class​​  ​​Bean1{​



​public​​  ​​int​​  ​​I = ​​ ​​0​​ ​​;​



​}​



 



​static​​  ​​class​​  ​​Bean2{​



​public​​  ​​int​​  ​​J = ​​ ​​0​​ ​​;​



​}​



​}​



 



​class​​  ​​Bean{​



​class​​  ​​Bean3{​



​public​​  ​​int​​  ​​k = ​​ ​​0​​ ​​;​



​}​



​}​




  从前面可知,对于成员内部类,必须先产生外部类的实例化对象,才能产生内部类的实例化对象。而静态内部类不用产生外部类的实例化对象即可产生内部类的实例化对象。

  创建静态内部类对象的一般形式为:  外部类类名.内部类类名 xxx = new 外部类类名.内部类类名()

  创建成员内部类对象的一般形式为:  外部类类名.内部类类名 xxx = 外部类对象名.new 内部类类名()

因此,(1),(2),(3)处的代码分别为:

2.下面这段代码的输出结果是什么?



​public​​  ​​class​​  ​​Test {​


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


​Outter outter = ​​ ​​new​​  ​​Outter();​


​outter.​​ ​​new​​  ​​Inner().print();​


​}​


​}​


 


 


​class​​  ​​Outter​


​{​


​private​​  ​​int​​  ​​a = ​​ ​​1​​ ​​;​


​class​​  ​​Inner {​


​private​​  ​​int​​  ​​a = ​​ ​​2​​ ​​;​


​public​​  ​​void​​  ​​print() {​


​int​​  ​​a = ​​ ​​3​​ ​​;​


​System.out.println(​​ ​​"局部变量:"​​  ​​+ a);​


​System.out.println(​​ ​​"内部类变量:"​​  ​​+ ​​ ​​this​​ ​​.a);​


​System.out.println(​​ ​​"外部类变量:"​​  ​​+ Outter.​​ ​​this​​ ​​.a);​


​}​


​}​


​}​



Java内部类详解_成员内部类_06

  View Code

 

  最后补充一点知识:关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:

  1)成员内部类的引用方式必须为 Outter.Inner.

  2)构造器中必须有指向外部类对象的引用,并通过这个引用调用super()。这段代码摘自《Java编程思想》



​class​​  ​​WithInner {​


​class​​  ​​Inner{​


 


​}​


​}​


​class​​  ​​InheritInner ​​ ​​extends​​  ​​WithInner.Inner {​


 


​// InheritInner() 是不能通过编译的,一定要加上形参​


​InheritInner(WithInner wi) {​


​wi.​​ ​​super​​ ​​(); ​​ ​​//必须有这句调用​


​}​


 


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


​WithInner wi = ​​ ​​new​​  ​​WithInner();​


​InheritInner obj = ​​ ​​new​​  ​​InheritInner(wi);​


​}​


​}​



作者: ​​海子​​

标签:部类,Java,内部,public,详解,Inner,Outter,class
From: https://blog.51cto.com/toutiao/5956059

相关文章

  • 浅析Java中的final关键字
    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法。......
  • Java 如何有效地避免OOM:善于利用软引用和弱引用
    想必很多朋友对OOM(OutOfMemory)这个错误不会陌生,而当遇到这种错误如何有效地解决这个问题呢?今天我们就来说一下如何利用软引用和弱引用来有效地解决程序中出现的OOM问题。下......
  • java中的模板设计模式【抽象类的最佳实践】
    本文主要讲述抽象类最佳实践,模板设计模式。老韩的介绍:示例代码如下:1/**2*需求:计算出执行一个job()方法,所需要花费的时间.3*使用模板设计模式【抽......
  • zk内部原理
    请求。事务和标志符请求客户端的只读请求exixtgetDatagetChildrenzk服务器本地处理后返回客户端客户端会改变服务器状态的请求createdeletesetData转发给群首......
  • [新手向]Java的一个新手天坑
    Java新手天坑!想写一篇新手避坑向的文章很久了,最近来到园子,终于有机会发表这篇文章了(文笔和技术都不咋地,大佬见谅,适用于刚入坑没多久的新手群体)在java中,经过一段学习的人......
  • JAVA Thread.sleep(0)深入理解
    JAVA并发-Thread.sleep(0)深入理解Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。通过调用Thread.sleep(0)的目的是为了让GC线程有机会被操作......
  • java -贪心
     给定一个字符串,一个K 可以交换相邻字符,最多K次。问可以得到的最大值如 Strings="01326";intk=7;  01326-->60132-->63012-->63102importjava.util......
  • 第01期:详解 Prometheu 专栏开篇
    开篇致辞大家好,从今天开始,我将开启一个全新的专栏叫做《详解Prometheus》。专栏会详细介绍Prometheus这款优秀的开源监控告警系统的使用,欢迎感兴趣的小伙伴关注!一、产品......
  • 第06问:内部临时表何时使用磁盘?
     问题:在 ​​实验05 ​​中,我们看到了内部临时表会使用到不少内存。那么如果需要的临时表再大一些,必然要使用到磁盘来承载,那么内部临时表是何时使用磁盘的? 实验:我们仍......
  • java.sql.Connection 线程安全吗?
    首先看一段代码:try(Connectionconnection=DriverManager.getConnection("jdbc:mysql://remote01:3306/test");){ ExecutorServiceexecutor=ThreadUtil.newExecut......