Unit03 - 对象内存管理 继承的意义(上)
Posted 唐胜伟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unit03 - 对象内存管理 继承的意义(上)相关的知识,希望对你有一定的参考价值。
Unit03 - 对象内存管理 、 继承的意义(上)
1.内存管理:由JVM来管理的
1)堆:
1.1)存储所有new出来的对象(包含成员变量)
1.2)没有任何引用所指向的对象就是垃圾,
垃圾回收器(GC)不定时清理垃圾,
回收过程是透明的(看不到的),
调用System.gc()建议GC尽快来回收
1.3)成员变量的生命周期:
创建对象时存在堆中,对象被回收时一并被回收
1.4)内存泄露:不再使用的对象没有被及时的回收
建议:对象不再使用时及时将引用设置为null
2)栈:
2.1)用于存储正在调用中方法的所有局部变量(包括参数)
2.2)调用方法时在栈中为该方法分配一块对应的栈帧,
栈帧中存储的是方法中的所有局部变量(包括参数),
方法调用结束时,栈帧被清除,局部变量一并被清除
2.3)局部变量的生命周期:
调方法时存在栈中,方法结束时与栈帧一起被清除
3)方法区:
3.1)用于存储.class字节码文件(包括方法)
3.2)方法只有一份,
通过this来区分具体的对象
2.继承:
2.1)作用:实现代码的复用,避免代码的重复
2.2)通过extends来实现继承
2.3)父类:所有子类所共有的属性和行为
子类:子类所特有的属性和行为
2.4)子继承父后,子具有: 父类的+子类的
2.5)一个父类可以有多个子类,
一个子类只能有一个父类----单一继承
2.6)继承具有传递性
2.7)java规定,构造子类之前必须先构造父类
若子类构造中不调用父类的构造,则默认super()调用父类的无参构造
若子类构造中调了父类的构造,则不再默认提供
super()调用父类的构造必须位于子类构造的第一句
3.super:指代当前对象的父类对象
super的用法:
1)super.成员变量名------访问父类的成员变量
2)super.方法名()--------调用父类的方法
3)super()---------------调用父类的构造方法
4.向上造型:
1)父类型的引用指向子类的对象
2)能点出来什么,看引用的类型
//老虎是动物
Animal o3 = new Tiger(); //向上造型
class Animal{ //动物类
}
class Tiger extends Animal{ //老虎类
}
JAVA内存变量:
编译期,只检查语法是否正确
编译错误,指的是语法错误
运行期,JVM登场----分配内存
变量----占用的是JVM的内存
JAVA垃圾管理:
没有任何引用所指向的对象
垃圾回收器(GC),不定时到内存堆中清理垃圾
并非看到垃圾就立刻清理
垃圾回收过程是透明的(看不到的)
System.gc()让内存回收尽量快一些
内存泄露:
没有用的内存还没有被及时的回收
若对象不用了,及时将引用设置为null
堆-1
堆-2
栈
方法区
程序一:
package oo.day03; public class Cell { int row; int col; Cell(int row,int col){ this.row = row; this.col = col; } String getCellInfo(){ return row+","+col; } }
package oo.day03; public class Tetromino { Cell[] cells; //格子数组,成员变量 // Cell[] cells=null; //如果没有下面cells = new Cell[4];声明操作,T.java里面会报索引错误 Tetromino() { cells = new Cell[4]; //创建格子数组对象 } void drop(){ for(int i=0;i<cells.length;i++){ this.cells[i].row++; } } void moveLeft(){ for(int i=0;i<cells.length;i++){ this.cells[i].col--; } } void moveRight(){ for(int i=0;i<cells.length;i++){ this.cells[i].col++; } } void print(){ for(int i=0;i<cells.length;i++){ String str =this.cells[i].getCellInfo(); System.out.println(str); } } }
package oo.day03; public class T extends Tetromino { T(){ this(0,0); } T(int row,int col){ //super(); //调用父类的无参构造---默认参数 cells[0] = new Cell(row,col); //创建格子 cells[1] = new Cell(row,col+1); cells[2] = new Cell(row,col+2); cells[3] = new Cell(row+1,col+1); } }
package oo.day03; public class J extends Tetromino{ J(){ this(0,0); } J(int n){ this(n,n); } J(int row,int col){ this.cells[0] = new Cell(row,col); this.cells[1] = new Cell(row,col+1); this.cells[2] = new Cell(row,col+2); this.cells[3] = new Cell(row+1,col+2); } }
package oo.day03; public class O extends Tetromino{ O(){ this(0,0); } O(int n){ this(n,n); } O(int row,int col){ this.cells[0] = new Cell(row,col); this.cells[1] = new Cell(row,col+1); this.cells[2] = new Cell(row+1,col); this.cells[3] = new Cell(row+1,col+1); } }
package oo.day03; public class TJTest { public static void main(String[] args) { // T t = new T(2,5); // printWall(t); Tetromino o1 = new T(2,5); //02.-1 向上造型 printWall(o1); //传值 J j = new J(1,6); //02.-2 printWall(j); //向上造型,同时传值 } //打墙+打T型-----方法二: // public static void printWall(T tt){ public static void printWall(Tetromino tt){ //01. 向上造型 // public static void printWall(T tt){ //01. 向上造型 // public static void printWall(J tt){ //01. 向上造型 for(int i=0;i<20;i++){ //控制行 for(int j=0;j<10;j++){ //控制行个数0 boolean flag = false; //1.假设打- // i = 2 j = 5 for(int k=0;k<tt.cells.length;k++){ //这里循环4次; if(i==tt.cells[k].row&& j==tt.cells[k].col){ //这里只取一次正确结果,如果有else,把“-”也会打上 flag = true; //因为有四个格子,所以循环4次,这里只取对的那次, break; } } if(flag){ System.out.print("* "); }else{ System.out.print("- "); } } System.out.println(); } /* //打墙+打T型----方法一 public static void printWall(T tt){ for(int i=0;i<20;i++){ for(int j=0;j<10;j++){ if(i==tt.cells[0].row&&j==tt.cells[0].col || i==tt.cells[1].row&&j==tt.cells[1].col || i==tt.cells[2].row&&j==tt.cells[2].col || i==tt.cells[3].row&&j==tt.cells[3].col){ System.out.print("* "); }else{ System.out.print("- "); } } System.out.println(); }*/ } }
增加打印“I am a T”
package oo.day04; //T型 public class T extends Tetromino{ T(){ this(0,0); } T(int row,int col){ super(); //调用父类的无参构造---默认的 super.cells[0] = new Cell(row,col); //创建格子对象 super.cells[1] = new Cell(row,col+1); super.cells[2] = new Cell(row,col+2); super.cells[3] = new Cell(row+1,col+1); } void print(){ //输出4个格子的行列号 System.out.println("I am a T:"); super.print(); //调用父类的print()方法 } }
程序二:(类继承的演示)
upTypeDemo.java
package oo.day03; public class UpTypeDemo { public static void main(String[] args) { Eoo o1 = new Eoo(); o1.e = 1; o1.show(); //o1.f = 2; //编译错误,父类不能访问子类 Foo o2 = new Foo(); o2.f = 1; o2.test(); o2.e = 2; //正确,子类可以访问父类 o2.show(); //正确,子类可以访问父类 Eoo o3 = new Foo(); //向上造型,语法:父类名字,子类的名字; o3.e = 1; //这不是造型,这是访问成员,访问东西 o3.show(); //o3.f = 2; //编译错误,能点出来什么,看引用类型 //o3对应的是Eoo类,所以只能使用Eoo下面的方法; } } class Eoo{ //Eoo是父类 int e; void show(){} } class Foo extends Eoo{ //Foo是子类,继承Eoo父类 int f; void test(){} }
课后作业:
1 简述JVM垃圾回收机制
参考答案
垃圾回收机制是Java提供的自动释放内存空间的机制。
垃圾回收器(Garbage Collection,GC)是JVM自带的一个线程,用于回收没有被引用的对象。
2 Java程序是否会出现内存泄露
参考答案
会出现内存泄漏,一般来说内存泄漏有两种情况。
一是在堆中分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉;
另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。
第一种情况,在Java中已经由于垃圾回收机制的引入,得到了很好的解决。所以,Java中的内存泄漏,主要指的是第二种情况。
下面给出了一个简单的内存泄露的例子。在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个List中,如果我们仅仅释放引用本身,那么List仍然引用该对象,所以这个对象对GC来说是不可回收的。代码如下所示:
List list=new ArrayList(10); for (int i=1;i<100; i++) { Object o=new Object(); list.add(o); o=null; }
此时,所有的Object对象都没有被释放,因为变量list引用这些对象。
3 JVM如何管理内存,分成几个部分?分别有什么用途?说出下面代码的内存实现原理:
Foo foo = new Foo(); foo.f();
参考答案
JVM内存分为“堆”、“栈”和“方法区”三个区域,分别用于存储不同的数据。
堆内存用于存储使用new关键字所创建的对象;栈内存用于存储程序运行时在方法中声明的所有的局部变量;方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入类文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区存储。
Foo foo = new Foo(); foo.f();
以上代码的内存实现原理为:
1.Foo类首先被装载到JVM的方法区,其中包括类的信息,包括方法和构造等。
2.在栈内存中分配引用变量foo。
3.在堆内存中按照Foo类型信息分配实例变量内存空间;然后,将栈中引用foo指向foo对象堆内存的首地址。
4.使用引用foo调用方法,根据foo引用的类型Foo调用f方法。
以上是关于Unit03 - 对象内存管理 继承的意义(上)的主要内容,如果未能解决你的问题,请参考以下文章