内存分析和常用类

马士兵的
内存解析:

基本类型占一块内存,引用类型占两块内存。
所有new出来的东西都在堆内存(heap segment)里面。 堆内存:运行期间动态分配内存,因为事先不知道分配多大,只有在运行(而不是编译)期间才知道分配多大。 方法只是一段代码(在 code segment里面),它只有被调用的时候才会占用内存。
引用 — 一小块内存指向一大块内存。 局部变量(c1)分配在栈内存(stack segment)(C c1 = new C();)成员变量是在堆中分配的,而且只有new出来的时候才会在堆中分配。
构造方法:
new的时候实际上去调用构造方法,顾名思义,把类构造成一个对象。必须和类同名并且没有返回值(void也不行)。
没有显示定义构造方法,编译器会自动定义一个空的。Point(){}; 一旦你显示定义了,编译器就不给你加默认的了。

这里写图片描述
分析:
方法调用完之后,栈内存为它分配的空间全部消失。— 只剩下一个引用(地址)指向堆中一个对象。
public class Test要和java文件名Test同名。

static声明的变量分配在data segment中(数据区)System.out out是静态的对象(PrintStream类)
字符串常量也在数据区。
这里写图片描述

权限:
很简单:protected 同一个包和子类能访问,。default是同一个包能访问。(记住”protected同包同子类,默认的同包” 就完了)
private和public就更简单了,private类内部才行,public任何地儿都行。
这里写图片描述

spuer引用父类对象,因为new子类对象的时候里面会有一个父类对象,那如何引用呢,就用super。
new一个对象出来的时候,它会有一个this引用指向自身,如果这个对象是子类对象那还会有一个super引用指向当前对象的父对象,


这里写图片描述>
这里写图片描述>

继承中的构造方法:
要构一个子类对象,那必须要先构一个父类对象。否则子类从哪来。所以子类构造方法中默认有一个super();调用父类无参的构造方法。
那Object类是所有的父类(默认class Person extends Object),它一定有一个空的构造方法,把它先造出来。
这里写图片描述

static:
Java里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类
普通类是不允许声明为静态的,只有内部类才可以,详情请问内部类的讲解
这里写图片描述

Object类:

Object
clone() –
finalize() – 有点类似于C++的析构函数 垃圾回收器回收前,会调用finalize方法。— 用的比较少,不必纠结。
getClass() — 可以认为是编译好的class文件,返回值是一个Class对象….小写的class是关键字,大写的是一个类
int hashCode() — 返回对象的哈希码。举例:对象可以根据哈希码很快找到自己在内存中的位置。
wait、notify、notifyAll – 线程间通信会涉及到
String toString() — 返回代表一个对象的字符串。 看下图第二条。。
这里写图片描述
Object的toString() 默认的实现是: 后半截是它的16进制的哈希编码。建议所有子类都重写
这里写图片描述
int hashCode() — 站在JVM的角度看,在内存里面有好多个对象,一个程序运行的时候会有很多对象在内存里分配,那JVM运行的时候就要找到这些个对象的地址,怎么找呢???
它有一张表来记录每个对象在什么位置上,而这个表一般是用哈希编码来记录的,每个对象都有自己独一无二的哈希码,根据这个哈希码就能找到对应的对象。但是JAVA对哈希码的实现有点问题,不同的俩对象的哈希码可能是相同的。讲Map的时候再说。
equals() – 重写父类Object如下:string也重写了,比较的是字符序列。。
这里写图片描述

Object类的一些方法:
public class T1 {
    public static void main(String[] args){
        Cat cat1 = new Cat(1,"y",10);
        Cat cat2 = cat1; //地址一样,肯定equals
        Cat cat3 = new Cat(1,"y",10);
        System.out.println(cat1);  //这里会自动调用cat的toString()方法。
//        System.out.println(cat1.equals(cat3));
//        testString();
    }

    /**
     * 用下string提供的方法
     * 1、String的toString方法是return this
     * 2、请熟练写出string的equals
     */
    public static void testString(){
        String s1 = "abc";
        String s2 = null;
        System.out.println(s1);
        System.out.println(s1 == s2);  //和null比地址,结果是false
        System.out.println(s1.equals(s2));  //object比较的是地址(也就是引用)
    }
}

class StringByMyself{
    private char value[];  //假设这就是string的value
    //String的equals
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        /**
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }*/
        return false;
    }
}
/**
 * 用一个cat 重写toString 和 equals
 */
class Cat{
    private int id ;
    private String color;
    private int weight;

    public Cat(int id, String color, int weight) {
        this.id = id;
        this.color = color;
        this.weight = weight;
    }

    @Override
    public String toString() {
        System.out.println(super.toString());  //看看地址是什么
//        return super.toString();   IDEA默认生成的toString,这个不怎么好,自己写一个
        return "id是" + this.id +" 颜色是"+this.color +" 体重是"+this.weight;
    }

    @Override
    public boolean equals(Object obj) {
//        return super.equals(obj); IDEA默认生成的toString,这个也不怎么好,自己写一个
        //模仿string的写一个
        if (this == obj) { //1、先比较地址,相同肯定true,不同肯定false
            return true;
        }
        //2、再比较值,逐个比较属性值
        if(obj instanceof Cat){
            Cat anotherCat = (Cat)obj;
            if(anotherCat.id == this.id &&
                    anotherCat.color == this.color &&
                    anotherCat.weight == this.weight){
                return true;
            }
        }
        return false;
    }
}
常用类

String
空的构造方法出来的是一个空串,不是空值。
这里写图片描述
要改变编码格式 的时候会用到它的一个构造方法如下。
传入一个字节数组,并指定编码格式。。这个例子可以看慕课网的IO那一节。

public String(byte bytes[], String charsetName){...//此处省略}

这里写图片描述

小例子:

/**
     * 指定字符串在原串中出现了几次。
     *思路:indexOf找到了就截取掉,然后再找
     */
    public int findSunStr(String sToFind,String s){
        int count = 0;
        int index = -1;
        while ((index = s.indexOf(sToFind)) != -1){
            s = s.substring(index + sToFind.length());
            count++;
        }
        System.out.println(count);
        return count;
    }

File代表文件或者路径名。new File()不表示在硬盘中就有一个文件了,而是内存中有一个对象而已
递归列出目录结构:直接f.listFiles()就行,如果isDirectory()再递归一下就行。
这里写图片描述

枚举 – 某一些类型必须只能取 某些值之一。否则报错
一扇门只能你和你老婆打开,你是1,你老婆是-1 。其余数字进来就抛异常。但是别人也可以把数字随便赋值。
与其在程序中限制,不如在编译的时候就限制好,所以枚举出现了。
再举例:设计一个游戏,只能规定上下左右四个方向,如果用int (1,2,3,4)表示可以是可以,但是如果写成5,在编译期间就发现不出来。还不如用枚举enum
注意是定义一个枚举类型,而不是一个变量。

高琪的
内存

这里写图片描述
属性的初始化:
八种基本类型,数字占了六种默认值是0,小数是0.0,boolean默认是false其实也是0,char:\u0000,其实也是0。
除了这八种,其余默认都是null。
return作用:方法返回值 和 结束方法
变量的作用域:
类里面,方法外面:成员变量。系统默认初始化,
方法里面:局部变量,这里需要手动初始化,否则使用的时候编译报错,不使用编译不报错。

这里写图片描述
方法区在堆里面,是堆的一部分。如下图所示:
记住一句话:操作对象就是操作它的地址。。

Student student = new Student(); 
Student -- JVM会去classpath路径下找这个类,找到了之后就用类加载器classloader加载,加载后,在方法区中就有了Student类的信息!
student --- 局部变量分配在栈内存
new Student() --- 会实例化一个对象在堆内存
= --- 赋值 这一步 就是把这个对象的地址(一个16进制的数)的值 赋给 student。  

Student s2 = new Student(); 下次看Student 这个类有了,就不会再去加载了。

这里写图片描述

GC垃圾回收机制

什么时候回收,如何回收。JVM已经有算法实现。
C++ : 自己回收 ,餐馆吃完饭,客人自己收。
JAVA:自己回收,餐馆吃完饭服务员回收垃圾(收盘子)
这里写图片描述

final

这里写图片描述

内部类

只供当前类使用,内部类可以直接访问外部类私有属性,但是外部类不可直接访问,看下面图片讲解。
这里写图片描述
非静态的(记住两行红字即可):
这里写图片描述
静态的(记住两行红字即可):
这里写图片描述

/**
 * 测试内部类的使用
 * @author dell
 *
 */
public class Outer {
    public static void main(String[] args) {
        Face f = new Face();
        Face.Nose n = f.new Nose();    //这样new才可以,必须先要有外部对象,才能有内部类对象
        n.breath();

        Face.Ear e = new Face.Ear();  //
        e.listen();
    }
}

class Face {
    int type;
    String shape="瓜子脸";
    static String color="红润";

    //这是非静态内部类
    class Nose {

        //这个方法不能设置为static,因为非静态内部类是从属于外部类对象的,是从属于一个对象的
        void breath(){    
            System.out.println(shape); //内部类可以直接访问外部的成员变量
            System.out.println(Face.this.type);  
            System.out.println("呼吸!");
        }
    }

    //这是静态内部类,可以看成是外部类的一个静态成员,它是从属于类的。不是对象的
    static class Ear {
        void listen(){
            System.out.println(color); //不能直接访问外部类的普通属性,但可以使用静态属性
            System.out.println("我在听!"); 
        }
    }
}

内部类分类:
1、成员内部类(当成外部类的一个成员)(静态和非静态,成员也有普通成员和静态成员),
重点看看这个
2、匿名内部类
启动一个线程就是。。new Thread( ).start():

你也可以如下创建一个Thread的匿名子类:
Thread thread = new Thread(){
   public void run(){
     System.out.println("Thread Running");
   }
};
thread.start();
多态
public class HttpServlet {
    public void service(){
        System.out.println("HttpServlet.service()");
        this.doGet();  //隐形的写了 this.doGet()。。写doGet()也可以。
    }

    public void doGet(){
        System.out.println("HttpServlet.doGet()");
    }
}
public class MyServlet extends HttpServlet {

    public void doGet(){
        System.out.println("MyServlet.doGet()");
    }

}
public class Test {
    public static void main(String[] args) {
        HttpServlet s = new MyServlet();
        s.service();  //这里的service是调用的是子类的doGet 。
    }
}

为啥调的是子类的?? 分析内存:
doGet()和 service()都有两个隐形的this,和super。这里的this指的就是子类。
只需要知道,里面的两个this指的都是最外面的对象也就是子类。所以调的是子类。
这里写图片描述

数组

数组也是对象,引用类型。除了那四类八种基本类型,其他的都是引用类型,包括数组。
数组里面元素的初始化规则 和 成员变量的初始化规则一样。
操作对象就是操作它的引用 很多类的底层其实都是数组。

String (string,stringbuffer,stringbuilder是数组的典型运用)

在Java中String类其实就是对字符数组的封装
《java中的String为什么是不可变的? – String源码分析》
博客解释:http://blog.csdn.net/zhangjg_blog/article/details/18319521
《 为什么String要设计成不可变的?》
博客解释:http://blog.csdn.net/renfufei/article/details/16808775
这里写图片描述
数组看完就可以看string源码了,因为底层就是操作数组
final可以修饰变量,方法,类。String类无法被继承
== 比较的是地址,你用一个类的toString打印出来看看就知道了

//初始化这个字符数组后不能再初始化,只能给value赋一次值
//注意:这个char是可以修改的,但是string没有提供暴露方法
//final只是引用的值不可变(),但是指向的对象的值可以变
private final char value[];  
//构造方法
public String() {
        this.value = new char[0];  //老的JDK,把长度为0的数组对象复制给value
        this.value = "".value;
    }
//String: 不可变的字符序列。。。  下面分析下源码
public class TestString {

    public static void main(String[] args){
        String s1 = "ddd";
        String s2 = "ddd";
        System.out.println(s1 == s2);  //常量池是放在方法区的,这里s1和s2指向是同一个常量,所以地址也是一样的
        System.out.println(s1.replace("*", "a"));
    }

    //string类的一个成员变量, 一个不可变(final)的字符数组。什么意思,就是只能给value赋一次值。
    //例如 A a = new A();栈内存的a只能赋值一次,a只能指向一个对象而不能指向堆中新的对象,但是a指向的对象的值是可以改变的。
    //所以final仅仅是a的值不能变,但是a指向的对象值是可以变的。
    // 这里的value初始化一个数组之后,不能再初始化别的数组。数组里面的值也不可变,因为没有提供可变的方法,value是private的,不让你改
    //也没有提供暴露的接口。String有getChar()方法,但是没有setChar().如果有setChar方法就可以改value了。虽然value是final的。
    //这就是为什么属性私有的原因,我想让你操作就提供set,不想让操作就不提供。这是JAVA封装的思想。
    private final char[] value;
    private int hash; // Default to 0

    //重载的构造函数 特别多
    public TestString() {
        this.value = new char[0];
    }

    /*
    public TestString(String original) {
        this.value = original.value;  //string的属性都是private的,只能通过方法去改
        this.hash = original.hash;
    }*/

    public TestString(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    public int length() {
        return value.length;
    }

    public boolean isEmpty() {
        return value.length == 0;
    }

    public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

    /*
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }*/

    public int indexOf(int ch) {  //返回字符所在索引位置,找不到就返回-1
        return indexOf(ch, 0);
    }

    //注意,返回的是一个新字符串 new string
    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return "";
        /*return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen); //这里构造一个新的string,如下*/
    }
    /*
    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);  还是用到了数组的拷贝
    }*/

    //常用的方法还有
//    public String replace(char oldChar, char newChar) {}
//  public String[] split(String regex, int limit) {}
    //public String trim() {.../省略}  **去除首位空格**,中间的不去

    public char[] toCharArray() {  //返回一个新的字符数组,这个可以改。 老的是final的,不能改
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }
    //Cannot use Arrays.copyOf because of class initialization order issues
    //这段注释是什么意思? 解释如下:
    作者注释写的很清楚了, 由于类初始化顺序的问题。
虽然String 和Arrays 都属性rt.jar中的类,但是BootstrapClassloader 在加载这两个类的顺序是不同的。
所以当String.class被加载进内存的时候,Arrays此时没有被加载,所以直接使用肯定会抛异常。而System.arrayCopy是使用native代码,则不会有这个问题。
另外你说的把代码换掉以后也能运行, 这时候 jvm已经加载完了所有的系统类, 所以你才会看到也能正常运行。

建议楼主去了解一下jvm 类加载器方面的知识 就明白了

}
Object的toString
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode()); 哈希码根据对象在内存中的位置生成,唯一不重复!!
    }

String的toString
public String toString() {
        return this;  //返回自身
    }
//这样拼字符串会产生1002个对象,很浪费空间。。尽量避免这样的代码
String gh = new String("a"); //这样就是1002个对象,因为new String也是一个对象。
String gh = "a";   //这样就是1001个对象,双引号字符串"a"是一个string对象。//内存中有1001个对象,gh就指向了循环最后的那个对象。
        for (int i = 0; i < 1000; i++) {
            gh = gh + i;   
        }
        System.out.println(gh);   //这种做法非常浪费空间,不可取。要避免字符串叠加这样的代码

这里写图片描述

stringbuilder – 记一个:builder线程不安全,效率高

关于append方法可以看博客:http://blog.csdn.net/qingmengwuhen1/article/details/69399751
查看Java8中StringBuffer的append()方法改变字符串的源代码:

public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

str.getChars()方法即是将str的所有字符拷贝到value[]的后面,返回的还是原来的value数组。
引申:
final意义:最终的,不可改变的。
  1、修饰变量,为常量,值不可变;
  2、修饰对象,值可变,引用不变; —– 注意这里。。。。
  3、修饰方法,方法不可重写;
  4、修饰类,无子类,不可以被继承,更不可能被重写。

/**
 * 测试可变字符序列。所谓可变:就是stringbuilder的char[] value是可变的。它没有final,而是默认的,同包可以访问。
 StringBuilder(线程不安全,效率高),StringBuffer和StringBuilder代码差不多(线程安全,效率低)
 一般作为局部变量,一般使用StringBuilder,用的多
 * String:不可变字符序列
 * StringBuilder 和 StringBuffer都是继承自 AbstractStringBuilder。 很相似
 *
 */
public class Test01 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();   //构造器什么都没传,但是实际上创建了一个长度为16的字符数组,看源码就知道了,
        StringBuilder sb1 = new StringBuilder(32);   //传32,那数组初始化长度就是32,不传就是16
        StringBuilder sb2 = new StringBuilder("abcd");  4+16=20//字符数组长度初始为16, value[]={'a','b','c','d',\u0000,\u0000...} \u0000是插入的默认值
        sb2.append("efg");//append就是把源string的字符数组拷贝到一个新的字符数组(就是stringbuilder里的char[] value )
        sb2.append(true).append(321).append("随便");   //通过return this实现方法链.

        System.out.println(sb2);

        System.out.println("##################");

        StringBuilder gh = new StringBuilder("a");  //数组初始化的时候长度是17
        for (int i = 0; i < 1000; i++) {
            //这里总共就两个对象(a一个对象,new StringBuilder一个对象) 
            //这里循环遍历修改对象,就是数组char[] value,修改的始终是同一个对象
            gh.append(i);    //初始化数组长度是17,那这么append1000次,超过了17怎么办? 它会扩容,扩容就是创建一个新数组 原来的长度乘以2再加2 
        }
        System.out.println(gh);
    }
}
/**
 * 测试StringBuilder的一些常用方法
 其实StringBuilder和StringBuffer说白了,就是数组的练习。char[] value;
 * @author dell
 *
 */
public class Test02 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("abcdefghijklmnopqrstuvwxyz");
        sb.delete(3, 5).delete(3, 5);   //结果:abchijklmnopqrstuvwxyz  3,5删除索引3和4的字符
        System.out.println(sb);
        sb.reverse();
        System.out.println(sb);
        //用法和StringBuilder一模一样,区别就是它的方法都加了synchronized,同步表示线程安全,
        //正因为安全,同步调用需要等待,效率就低了。   不加synchronized,调用不需要等待,所以效率高
        StringBuffer sb2 = new StringBuffer();  

    }
}
        String s1 = "hello";
        String s2 = "world";
        String s3 = "helloworld";
        System.out.println(s3 == s1 + s2);  //false。这里是s1+s2又会生成一个对象。
        System.out.println(s3.equals(s1 + s2)); //true
        System.out.println(s3=="hello"+"world"); // 这个是true。这里内存里还是指向常量池。
        System.out.println(s3.equals("hello" + "world")); //true
基础类型包装类

这里写图片描述

注意和string的互转。String.valueOf和 Integer.parseInt

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
public static String valueOf(Object obj) {
        return (obj == null) ? "null" : obj.toString();
    }

这里写图片描述

Integer重写了equals 
public Integer(int var1) {
        this.value = var1;     //所谓包装类,就是把int的值包装起来了,放到value中
    }  

public boolean equals(Object var1) {
        if (var1 instanceof Integer) {
            return this.value == ((Integer)var1).intValue();
        } else {
            return false;
        }
    }

装箱拆箱 —- 就是编译器帮你做的事儿
为什么需要? 因为自己转来转去太麻烦了。。。。
编译器自动地帮你做了转型。
这里写图片描述

缓存问题:
为什么这个范围的会当作基本类型处理? 因为这个范围的数字使用的视频非常的高,为了提高效率,基本类型再怎么也比对象快
下面这个小程序,d3和d4就不是两个对象了,所以d3==d4 是true
关于缓存,下面这个小例子说明的还不多。看看下面这个博客:
《 一道面试题关于Integer的缓存范围(-128~127)所引起的一系列问题记录》
https://blog.csdn.net/BeauXie/article/details/53013946
这里写图片描述

内部类高琪

就理解为,外部类的一个成员,和外部类的成员是平级的。
为什么需要内部类,就是2点:1、内部类可以方便的使用外部类的成员 2、内部类的存在就是为了辅助外部类,完成某些功能。
这里写图片描述

静态内部类中:不能调用外部类的普通成员,只能调用静态成员(也就是静态属性和静态方法)。 好理解吧
不相关的类中,可以直接创建。下图第三点
这里写图片描述

package com.nestedClass;
import com.nestedClass.Outer02.StaticInnerClass;

public class Demo02 {
    public static void main(String[] args){

        Outer02.StaticInnerClass osic = new Outer02.StaticInnerClass();
        //这种等价于上面,只是引入的时候不一样。引入的时候把com.nestedClass.Outer02看出是包名即可。。
        //可以直接创建
//   -  StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

class Outer02{

    int c = 5;
    static int d  = 10;

    public static class StaticInnerClass{
        int a = 3;
        static int b = 5;

        public void test(){
            System.out.println(d);
        }
    }

}

普通内部类: (和普通内部类的区别就是它依赖于外部类的对象,对象。它就不能直接创建了。。。)
这里写图片描述

public class Demo03 {
    public static void main(String[] args){
        //要这么用才行,不能直接InnerClass innerClass = new InnerClass();
        Outer03.InnerClass innerClass = new Outer03().new InnerClass();
//        InnerClass innerClass = new InnerClass();   报错。。
    }
}

class Outer03{
    private int c = 5;
    static int d  = 10;

    /*static */void ttt(){
        InnerClass innerClass = new InnerClass();   //因为InnerClass是属于外部对象的,所以这里加static报错。。。
    }

    class InnerClass{
        int a = 3;
//        final static int b = 5;  //成员内部类不能有静态成员,除非声明为final,并且只能是编译器可以确定值的常量表达式
//        final static Date date = new Date();  这种也不行,因为new是运行期间才确定的。

        public void test(){
            System.out.println(d); //成员内部类可以访问外部类所有的成员。。
        }
    }

}

匿名内部类:只使用一次的话才适合使用,一般用在方法里。因为匿名,没有名字,下次再用的话,就没有一个引用可以用。

关于静态内部类,和static。可以参考一篇博客:
《[面试经验]一个草根程序员如何进入BAT》写的很好。。
http://mp.weixin.qq.com/s?__biz=MzAxNzMwOTQ0NA%3D%3D&mid=2653354762&idx=5&sn=d5495b57cd953853f6108fdd7d771a78&chksm=8035d161b74258778a8a4bde7f3594a93b9a5cdb86b2dcc8ce797d0bada6e2808357f20b4710
静态内部类不依赖于外部类的实例存在,因此只需要直接创建内部类的实例就可以了,所以只有一个new关键字

static收集

1、static变量在JAVA中是属于类的,它在所有的实例中的值都是一样的。当类被JAVA虚拟机加载的时候 ,会对static变量进行初始化。
2、

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值