类结构:变量和方法 被static修饰的:是类的属性与方法 -----静态变量和静态方法 非static修饰的:是对象的属性与方法 ----成员变量和成员方法 静态方法或者成员方法内声明的变量 ---局部变量(必须初始化) 区别:1.类属性和方法被所有的对象共享,如果一个对象改变他的 值,那么其他对象访问的都是改变后的值。 2.成员变量和方法不能通过类名直接调用, 必须创建对象后方可调用,他被指定的对象所特有, 如果一个对象改变他的值 那么其他对象访问的值不受影响。 静态和成员之间的调用关系: 1.静态函数不能调用成员变量或成员函数(因为在类加载时非静态方法区只是母版区,不可用, 就算可用,在堆内生成了多个对象,他也不知道调的是哪一个) 2.成员函数可以调用静态变量或静态函数(因为在类加载时静态方法区是实实在在存在的,可用的) ----静态代码块:static{ 。。。。 } 静态代码块的作用:类一旦被加载就会调用,常用来测试代码, 使用案例如下: class A{ static{ System.out.println("类A被加载了。。。"); } } 静态和非静态内存分配: 知识补充:类的主要4个组成部分:静态方法和函数,非静态的方法和函数 1.程序跑起来会产生一块运行时数据区, 里面包含:栈,堆,方法区 2.接着将Dorm类加载进内存,而Dorm类中既有静态变量和方法,也有成员变量和方法, Dorm类被加载时(未创建对象),就会在方法区里面开辟一段内存用来存放dorm对象, 该内存被分为两部分,一部分用来存放静态的变量和方法,另一部分则用来 存放非静态的变量和方法, -------两块内存的区别: 类一旦被加载,静态方法区是实实在在存在的,可用的,而非静态区则只是作为母版区, 是不可用的,即非实在的,那该区什么时候可用呢?答案是:当类创建对象的时候。 3.当类创建对象时,会在堆里面产生一块内存存放该对象,并且该对象的内容就是母版区内容的复制, 同理当创建了对个对象时,在堆里就会分配多个内存,并且都是母版区的复制, 4.存在于栈内的main对象中包含多个对象的声明,每一个分别指向对应的堆内的内存, 但是却指向同一个静态方法区,即共享静态方法区,所以修改静态成员的值会影响 其他对象访问,但修改成员变量或方法,只是改变了某个对象堆内的数据,因为指向不同, 所以其他对象不受影响。 -----类加载进内存的时机: 1.运行这个类的main主方法时 2.其他的类调用这个类的静态变量或者静态函数时 3.创建对象时 4.使用反射 -- 如JDBC中的Class.forName()的使用 -----类加载时的内容以及顺序: 1.类加载只加载静态的 2.静态变量,静态函数,静态代码块static{} 类加载时会按照声明顺序,加载静态变量和静态代码块 而静态函数只是被加载进静态方法区,只有被调用时才会运行 构造函数: 1.构造函数是用来创建对象的, 一般创建对象默认调用的是该类的无参的构造函数 2.构造函数还可以用来在创建对象时对对象进行初始化 3.构造函数没有static修饰,但却是静态的 4.当创建一个类时,如果没有定义构造函数,那么系统会默认 提供一个无参的构造函数 5.当创建类的时候自己创建了一个有参的构造函数,默认的无参的 构造函数就变得不可用,除非同时定义一个有参的和无参的构造函数 -----堆内的对象的加载时机: 构造函数被调用时 对象在堆内被创建时对象要加载的内容: 1.构造代码块 --- 就是一个块语句,使用大括号括起来就行 2.成员变量 3.成员函数 如下类: class A{ public A(){ System.out.println("对象被创建。。"); } public String str = "成员变量"; { System.out.println("构造代码块。。。"); } public void test(){ System.out.println("成员函数。。。"); } } A a = new A(); 创建对象执行的流程: 1.加载类 2.调用构造函数产生栈帧 3.在堆中创建对象 在堆中创建对象要加载哪些内容: ---按照声明顺序加载成员变量和成员代码块 4.执行构造函数的方法体 5.将创建好的对象赋值给引用 ------类与对象的加载: 面试题如下: public class A{ public static void main(String[] args){ Load load1 = new Load(); Load load2 = new Load(); } } class Load{ //静态代码块:类一旦加载就会被调用 static{ System.out.println("Load类被加载了。。。"); } //构造代码块:只有在创建对象的时候才会被调用 { System.out.println("对象被加载了。。。"); } } 根据以上笔记对类和对象加载时机的说明以上程序的运行结果为: Load类被加载了。。。 对象被加载了。。。 对象被加载了。。。 结果总结: 1.类只会被加载一次,即加载进方法区并分为静态和非静态, 静态内容只在方法区加载一次,而非静态的则在每次堆内创建对象时对会被加载一次 2.对象每被创建一次就加载一次,对象的每一次加载都是对非静态方法区的复制, 在堆内开辟新的空间 ----完整地类与对象加载示例: 面试题: public class A{ public static void main(String[] args){ B b = new B(); } } class B{ //静态方法区内容---------------------------------------------- //静态变量 public static String svar = C.getStaticVar(); //静态方法 public static void staticMethod(){ System.out.println("静态方法。。。"); } //静态代码块 static{ System.out.println("静态代码块。。。"); } //构造方法 public B(){ System.out.println("构造方法。。。"); } //--------------------------------------------------- //母版区(非静态方法区)内容----------------------------- //1.构造代码块 { System.out.println("构造代码块。。。"); } //2.成员变量 public String var = C.getVar(); //3.成员函数 public void method(){ System.out.println("成员方法。。。"); } } class C{ public static String getStaticVar(){ System.out.println("生成静态变量。。。"); return "staticVar"; } public static String getVar(){ System.out.println("生成成员变量。。。"); return "var"; } } 执行流程分析如下: 1.对象在被创建时先加载类,加载类时按照声明顺序加载静态代码块和静态变量, 而静态函数不运行,只有在调用时才运行。 2.静态区加载完毕然后开始创建对象,即加载对象进堆内,对象加载时 要加载构造代码块和成员变量和成员方法,成员方法同样也是 只被加载不被执行,在被调用时才执行。 3.等对象在堆内创建完毕,整个构造函数的执行过程就结束了,然后 再执行栈帧里面的构造函数方法体。 3 --- 3 --- 1 类静态区 --- 对象 --- 构造方法 以上程序的运行结果为: 生成静态变量。。。 静态代码块。。。 构造代码块。。。 生成成员变量。。。 构造方法。。。 -------关键字final的使用--- 我们知道类变量(即被static修饰的变量)被所有类对象共享,并且在其他类中可以通过 类名点的方式进行调用,这就存在问题:其他类在调用某个类的类变量时在调用之后修改了 他的值,这是我们所不愿看到的,那么如何避免类变量的值被修改呢? 答案是:使用关键字final修饰,并且规定:被final修饰的变量名称各个字母均要大写 class A{ public final static int NUMBER = 4; } final修饰类:----- 此类不可以被继承,例如String类和所有的包装类(Integer等),还有数学类Math 注意:Arrays类没有被final修饰,但是仍然不可以被继承,设置类不可被继承的其他方法 final修饰变量:---- 修饰静态变量时必须进行初始化,因为类一旦被加载静态字段就加载了,所以加在之前必须初始化 修饰成员变量时,因为成员变量只有在对象创建完成之后才可以使用,所以必须在对象 创建完成之前进行赋值 对象创建完成之前----- A.构造代码块 B.构造函数体 final修饰方法:----不能被重写 函数修饰不可以同时出现final(不能重写)和abstract(要求重写),产生矛盾 -------int类型和包装类Integer类 int类型变量默认初始值为0 Integer类型默认初始值为null 注意:在javabean中创建对象模型的时候,编号id建议使用Integer类型,而不建议使用int类型 Integer b; //将Integer转为int int a = (int)b; -------String类:不属于8大基本数据类型,并且该类被final所修饰 String类型用来表示一个字符串,底层实现实际上是一个字符数组 基本数据类型的包装类操作的是一个基本类型数据, 与基本数据类型的包装类不同,String也可以看做一个包装类,他操作的 是字符数组。 -------字符串创建时的内存分配 ----- 方法区中存在字符串常量池,池中放的都是字符数组 1.直接赋值方式创建字符串 首先查找字符串常量池,有没有对应的字符串,如果没有,在常量池中创建, 然后指向这个字符串对象,如果有就直接指向其地址值 2.new 的方式创建字符串 首先在堆中开辟一块空间,查找字符串常量池,看有没有对应的 字符数组,如果没有,在常量池中创建,然后指向 如果有,直接指向. 面试题:String str = new String("abc"); String str2 = new String("abc"); ------执行第一条语句实际上创建了两个对象:堆内和字符串常量池中 接着再执行第二条语句只会在堆中创建一个对象,然后指向字符串常量池中的字符数组 关键是后者公用前者在字符串常量池中创建的字符数组“abc” 例如如下程序: String str1 = "abc"; String str2 = "abc"; String str3 = new String("abc"); String str4 = new String("abc"); 那么:str1 == str2 :true str3 == str4 :false