讨论??内部类与外部类的关系。
以下代码编译通过,但运行时会有java.lang.StackOverflowError 异常。
但如果将 //1 改为:private static Scroll acc = new Scroll();
//2 改为:private static class Scroll extends Test{
编译运行均可通过。
能解释下这是为什么吗?欢迎讨论。
public class Test implements Cloneable{
int i = 0;
private Scroll acc = new Scroll(); // 1
public Object clone(){
try{
return super.clone();
}catch ( CloneNotSupportedException e){
return null;
}
}
public Scroll getScroll(){
return acc;
}
public static void main(String[] args){
Test t = new Test();
Test back = t.getScroll();
System.out.println("haha.");
}
private class Scroll extends Test{ // 2
private int i = 0;
public void setInt( int i){
this.i = i;
}
public int getInt(){
return i;
}
}
}
问题点数:100、回复次数:30Top
1 楼helpall(was jl)回复于 2005-06-14 12:42:39 得分 40
1没有static,则每个Test实例都拥有一个acc变量,每个acc变量又是一个Test的子类Scroll的实例,而该实例又拥有一个acc变量,...,最后堆栈溢出.
1改为static后,每个Test实例共用一个acc变量.该变量是Test的子类Scroll的实例,而此实例还是共用该acc变量. 换句话说,acc是在类一级的,不是与实例相连的. 在下例中,三个acc都是一个东西:
Test a = new Test();
Test b = new Test();
Test.acc == a.acc; // true
Test.acc == b.acc; // true
当然,acc是私有的,不能在外部存取.
至于2改成static是因为Scroll是内部类,当1要用static存取时,2一定要用static定义.如果Scroll放在类的外部就可不必用static.Top
2 楼cat_871017(零下九度)回复于 2005-06-14 14:19:33 得分 10
上面这位大哥讲的实在是太详细了,我没什么可说的了
那就再讲的"通俗"一点吧
因为jvm为子类分配内存的时候是这样的:它先找到一块内存区域存放其父类的相关信息,然后接下来才是子类增加或修改的信息.比如说一 A继承B,那么A的内存里面其实有一块存着B.
而你的这个程序把一个子类作为父类的内部类,这样父类的内存里要分配一块给子类,而子类又要存放一个父类,这样无限嵌套循环,最后导致溢出了.
改为static 就好了,因为在嵌套时无论怎么样也只会产生一个内部类的实例,所以就终止了上面那种循环.
Top
3 楼Sanco(十扇门)回复于 2005-06-14 19:48:27 得分 0
就算 Scroll 不扩展 Test 类 ,程序同样会溢出。同意吗?不信试试Top
4 楼helpall(was jl)回复于 2005-06-14 19:56:49 得分 0
我觉得不应该, 至少下例没有问题.
class Outer {
private Inner acc = new Inner();
public static void main(String[] args){
Outer t = new Outer();
System.out.println("haha.");
}
class Inner { }
}Top
5 楼Sanco(十扇门)回复于 2005-06-14 20:08:36 得分 0
我检查下先Top
6 楼Sanco(十扇门)回复于 2005-06-14 20:15:30 得分 0
哦。是我看错了。
谢谢 helpall(was jl) 的指正。
因为我在代码里有加了个本身的对象实例。
class Test{
Test t = new Test();
...
}Top
7 楼Sanco(十扇门)回复于 2005-06-14 20:17:39 得分 0
我想其实跟原来的是一个道理。
t -〉acc -〉acc -〉acc -〉。。。。Top
8 楼Goldrush(上天有好生之德)回复于 2005-06-15 01:34:04 得分 0
dingTop
9 楼Sanco(十扇门)回复于 2005-06-15 12:08:21 得分 0
也就是说 一个类不能拥有 非静态的 本类对象,直接或间接子类对象。
再回到内部类:静态或非静态内部类中的私有成员都能被外部类所使用(反过来,外部类的私有成员也能被非静态内部类使用),通过反射机制,看到内部类有个指向外部类对象的this$0隐式指针,然而外部类却没有指向内部类的指针,?那外部类是怎么访问到内部类的呢??Top
10 楼helpall(was jl)回复于 2005-06-15 20:29:05 得分 0
"也就是说 一个类不能拥有 非静态的 本类对象,直接或间接子类对象。"--------好象不对,见下例
class Outer {
public Outer o;
public static void main(String[] args){
Outer t = new Outer();
Outer t1 = new Outer();
t.o = t1;
System.out.println("haha.");
}
}Top
11 楼Sanco(十扇门)回复于 2005-06-15 20:59:33 得分 0
你所说的 public Outer o;
o 只是变量而已,它并没有被实例化,也就是说它还没指向一个对象。
换成 public Outer o = new Outer(); 试试。将出现Overflow
Top
12 楼faen(发恩)回复于 2005-06-15 21:51:46 得分 0
呵呵,学习了Top
13 楼helpall(was jl)回复于 2005-06-15 22:59:42 得分 0
Sorry, there's no Chinese input software available here.
Please look into main().
t.o = t1;
This statement makes t owns a non-static object t1 of same class.
I can understand "public Outer o = new Outer(); " throws Exception. But the statement,"一个类不能拥有非静态的本类对象,直接或间接子类对象。", is incorrect. Think about a Tree-Node example, it's very common that a Node owns parent and children, which are non-static.Top
14 楼laughsmile(海边的星空)回复于 2005-06-16 11:18:46 得分 40
1.那外部类是怎么访问到内部类的呢??
外部类访问内部类的方法与访问本类中的复合类相同,也是把它当作自己的一个成员.
eg:
class A {
class B{
class C{
void doit(){}
}
}
}
class SeeResult{
public static void main(String[] args){
A.B.C c = ((new A()).new B()).new C();
c.doit();
}
}
2. 一个类不能拥有 非静态的 本类对象,直接或间接子类对象
其实这个原理跟递归是一样的,Tree-Node example就是一种递归,如果递归的时候没有退出条件,那当然是stack overflow."一个类不能拥有 非静态的 本类对象,直接或间接子类对象"这种说法就相当于说不能用递归.其实这还是可以使用的,只是要设置好退出条件.Top
15 楼helpall(was jl)回复于 2005-06-16 11:28:38 得分 0
严重同意laughsmile(海边的星空).
这一层意思讲得棒极了.要分的话我就另开贴给分.Top
16 楼Sanco(十扇门)回复于 2005-06-16 21:01:55 得分 0
谢谢各位。
谢谢helpall(was jl) 和 laughsmile(海边的星空)
看这样说怎样:一个类的成员,不能是非静态的(本类或者直接或间接子类的)实例,但可以是静态或非静态的指向本类或其它任何类的指针变量。
有关内部类到外部类,然后又到超类和接口的所有问题,都是由于本人想实现java中对象应用sizeof方法而来,其实有一种比较简单的方法,不停的实例化对象,然后看用了多少内存(我好像讲的太简单了^-^)。
但这种方法在很多场合都不很实用(特别是小内存,图形对象来讲),而且对理解对象的实现机制也没什么帮助。所以参考了网络上广为流传的另一种方法,相信大家都耳熟能详了。
目前还没什么进展,文章写得太涩,所以一时间弄得有点晕。
Top
17 楼nimifeng(学海无涯.......苦作舟....理解是美!!!Mars.Neil)回复于 2005-06-16 21:48:09 得分 0
学习..帮顶.Top
18 楼helpall(was jl)回复于 2005-06-16 23:09:40 得分 0
There's no 指针 concept in java. 指针 concept in java is called reference. The difference between a 实例 and a reference is that a 实例 is an initialized reference.
I still don't like the statement you made. :-) The important thing is that you can understand the concept. Sometime when a statement is made, the principle is twisted.
People will get confused when they see you talk about 指针变量 in java, unless you want C++ users to read it.Top
19 楼zhoulinjulian()回复于 2005-06-17 00:24:46 得分 0
又学到东西了,i love csdn!Top
20 楼zhaoce(朋苍.月友)回复于 2005-06-17 09:20:07 得分 0
看这样说怎样:一个类的成员,不能是非静态的(本类或者直接或间接子类的)实例,但可以是静态或非静态的指向本类或其它任何类的指针变量。
-------------------------------------------------
怎么不行?
给一个idea中缺省的单子模式
不是不能定义自己一个实例,关键是你要限制他的递归操作
不能无限制地递归下去
public class T {
private static T ourInstance = new T();
public static T getInstance() {
return ourInstance;
}
private T() {
}
}
Top
21 楼zhaoce(朋苍.月友)回复于 2005-06-17 09:22:49 得分 0
不好意思,看错了
前面说错了Top
22 楼zhaoce(朋苍.月友)回复于 2005-06-17 09:32:34 得分 10
这下可以了,写了一个新的例子,调试通过
前面例子写得不好,不要意思^_^
public class T {
int i=0;
private T ourInstance ;
public T getInstance() {
return this.ourInstance;
}
private T(int i) {
this.i=i;
if(i==0){
i++;
this.ourInstance= new T(i);
this.ourInstance.i++;
}
else{
}
}
public static void main(String[] args){
T t=new T(0);
}
}
Top
23 楼laughsmile(海边的星空)回复于 2005-06-17 11:29:44 得分 0
to helpall(was jl)
谢谢夸奖,呵呵,如要给我分,那当然是多么多么感谢哦。
to Sanco (God of Tmac)
给你介绍一个sizeof程序
public class Sizeof
{
public static void main (String [] args) throws Exception
{
// Warm up all classes/methods we will use
runGC ();
usedMemory ();
// Array to keep strong references to allocated objects
final int count = 100000;
Object [] objects = new Object [count];
long heap1 = 0;
// Allocate count+1 objects, discard the first one
for (int i = -1; i < count; ++ i)
{
Object object = null;
/* Instantiate your data here and assign it to object*/
//object = new Object ();
object = new Integer (i);
//object = new Long (i);
//object = new String ();
//object = new Byte( (byte) 0);
//object = new Float( 0.2f);
//object = new Double( 0);
if (i >= 0)
objects [i] = object;
else
{
object = null; // Discard the warm up object
runGC ();
heap1 = usedMemory(); // Take a before heap snapshot
}
}
runGC ();
long heap2 = usedMemory (); // Take an after heap snapshot:
final int size = (int)Math.round ( ((double)(heap2 - heap1)) /count);
System.out.println ("'before' heap: " + heap1 +
", 'after' heap: " + heap2);
System.out.println ("heap delta: " + (heap2 - heap1) +
", {" + objects [0].getClass () + "} size = " + size + " bytes");
for (int i = 0; i < count; ++ i) objects [i] = null;
objects = null;
}
private static void runGC () throws Exception
{
// It helps to call Runtime.gc()
// using several method calls:
for (int r = 0; r < 4; ++ r) _runGC ();
}
private static void _runGC () throws Exception
{
long usedMem1 = usedMemory (), usedMem2 = Long.MAX_VALUE;
for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++ i)
{
s_runtime.runFinalization ();
s_runtime.gc ();
Thread.yield ();
usedMem2 = usedMem1;
usedMem1 = usedMemory ();
}
}
private static long usedMemory ()
{
return s_runtime.totalMemory () - s_runtime.freeMemory ();
}
private static final Runtime s_runtime = Runtime.getRuntime ();
} // End of class
Top
24 楼helpall(was jl)回复于 2005-06-17 19:46:13 得分 0
laughsmile(海边的星空),请到
http://community.csdn.net/Expert/topic/4090/4090085.xml?temp=.1563837
接分.Top
25 楼Sanco(十扇门)回复于 2005-06-18 16:16:54 得分 0
非常感谢 各位的热心,特别是 laughsmile(海边的星空),helpall(was jl) 两位的激烈讨论。
我想也该结贴了。
to laughsmile(海边的星空):
谢谢你最后给的例子,这个例子得到的是对象的精确的size,耗的内存比较大,并且不太适合小内存容量的图形对象。
其实有另一种方法,通过分析对象的继承关系来得到对象的size的近似值。方法在下面网址:
http://www.javaworld.com/javaworld/javaqa/2003-12/02-qa-1226-sizeof.html(要是有精炼的语句可以概括这种方法,一定发消息给我,我一定另开贴给分。)
to helpall(was jl):
谢谢你的一些概念性的讲解。虽然我仍然将java中的变量用c中的指针来理解(希望我的方式没有误人子弟)。
Top
26 楼lvcheng606717(旅程)回复于 2005-06-19 12:26:14 得分 0
标记学习Top
27 楼wtobias(猪猪)回复于 2005-06-30 19:57:26 得分 0
xuexiTop
28 楼interhanchi(on the Java Road)回复于 2005-07-17 02:00:26 得分 0
markTop
29 楼songsong1982(松松)回复于 2005-09-01 14:33:19 得分 0
学习学习
Top
30 楼cyxlsm()回复于 2005-09-06 15:15:52 得分 0
static
使用static关键字,可以指定一个成员变量为一个类变量或者指定方法为类方法,类方法不能访问实例变量,除非方法先创建类的一个实例并且通过它来访问变量。实例成员和类成员之间的另外一个差别是类成员可以从类本身进行访问。你不必实例化类来访问它的类成员。
static方法不用创建对象就能访问,仍能访问调用一个static方法
原因就是它是类方法,当然不用创建对象就能被访问
注意:类方法与实例方法的区别
类名+方法
Top




