
通过题目也可以学习很多。
不断总结,不断提升,刷新自己的知识库。
方法中的内部类能不能访问方法中的局部变量,为什么?
Java中的内部类分四种:成员内部类、局部内部类、静态内部类和匿名内部类。
局部内部类: 即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。
注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。
先看一段代码:
1 | public class Test1 { |
注意:
在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。
要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。
通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。
综上所述,再重新看待这道题:
所谓“局部内部类”就是在对象的方法成员内部定义的类。
而方法中的类,访问同一个方法中的局部变量,是天经地义的。(即方法中的内部类能访问方法中的局部变量,然而还有一个条件那就是方法中的局部变量得被final修饰)
那么为什么要加final的局部变量才可以被访问呢?
从生命周期的角度看:
内部类的生命周期和方法中的局部变量是不一样的,内部类是也是一个类,是存储在堆中,也只有当对该类的引用消失时,内部类才会消亡。
而方法的局部变量是存储在栈中的,当调用结束时就会退栈,即在内存中这个属性就消失了。
也就是说,内部类的生命周期超过了方法中局部变量的生命周期,内部类可能会调用到已经消失的属性,因此内部类不能访问方法中的局部变量。
那么为什么要加final的局部变量才可以被访问呢?
通过将生成的class文件反编译可以知道:如果局部变量的值在编译期间就可以确定,则直接在局部内部类里面创建一个拷贝。
如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。
那么新的问题来了,在方法中的内部类访问到的方法中的变量和实际方法中的变量是不是同一个变量?
对,会造成数据不一致性,这样就达不到原本的意图和要求。
为了解决这个问题,java编译器就限定必须将方法中的变量设置为final变量,不允许对此变量进行更改(对于引用类型的变量,是不允许指向新的对象),这样数据不一致性的问题就得以解决了。
归纳上述回答的真正核心是:局部内部类对象中包含有要访问的final型局部变量的一个拷贝,成为它的数据成员。
因此,正是在这个意义上,final型局部变量的生命期,超过其方法的一次调用。
严格来说,方法调用结束,所有的局部变量(含final)全死亡了。
但局部内部类对象中有final型局部变量的拷贝。(由于该变量由final修饰,所以一致性的问题也被解决了)
ArrayList list = new ArrayList(); 在这个泛型为Integer的ArrayList中存放一个String类型的对象。
编译器编译带参数类型说明的集合时会去去除掉“类型”信息,使程序运行不受影响。
对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
由于编译生成的字节码会去掉泛型的类型信息,因此只要能跳过编译器,就可以往某个泛型集合中加入其他类型的数据。
1 | public class Test2 { |
请说明Java中字符’\’的含义,有什么作用?
在Java中反斜杠’\’代表的是转义字符。
转义字符:通过’\’来转变后面字母或符号的含义。
它用于描述一些无法用单个字符描述的特殊符号,如换行符,退格符,制表符等等(他们在window中分别是\n,\b,\t)。
当遇到一些java语法中被占用的特殊字符的时候,要想原样的输出,就需要在这些特殊的字符前面加入反斜杠。
比如反斜杠本身,单引号,双引号,小括号等,因为语法已经占用,所以要想原样的输出,只有在前面在加一个反斜杠。
这点在读取windows中文件的字符串路径时体现的特别明显,要想路径有效就要在单个反斜杠前在加一个反斜杠用于转义。
常用转义字符:
1 | \b:退格 |
API上如下描述:
反斜线字符 (‘\’) 用于引用转义构造,如上表所定义的,同时还用于引用其他将被解释为非转义构造的字符。因此,表达式 \ 与单个反斜线匹配,而 { 与左括号匹配。
假如我们在开发一个系统时需要对员工进行建模,员工包含 3 个属性:姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。
1 | /** |
定义一个文件输入流,调用read(byte[] b)方法将exercise.txt文件中的所有内容打印出来(byte数组的大小限制为5)。
汉字在国标码中的存储为1个字为2个字节。
用普通方法会出现乱码问题
解决办法:ByteArrayOutputStream:可以捕获内存缓冲区的数据,转换成字节数组。
此类实现了一个输出流,其中的数据被写入一个 byte 数组。
缓冲区会随着数据的不断写入而自动增长。
可使用 toByteArray()和 toString()获取数据。
关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。
1 | public class Test5 { |
把以下IP存入一个txt文件,编写程序把这些IP按数值大小,从小到大排序并打印出来。
- 61.54.231.245
- 61.54.231.9
- 61.54.231.246
- 61.54.231.48
- 61.53.231.249
1 | public class Test6 { |
写一方法,打印等长的二维数组,要求从1开始的自然数由方阵的最外圈向内螺旋方式地顺序排列。 如: n = 4 则打印:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
1 | public class Test7 { |
编写一个程序,它先将键盘上输入的一个字符串转换成十进制整数,然后打印出这个十进制整数对应的二进制形式。这个程序要考虑输入的字符串不能转换成一个十进制整数的情况,并对转换失败的原因要区分出是数字太大,还是其中包含有非数字字符的情况。提示:十进制数转二进制数的方式是用这个数除以2,余数就是二进制数的最低位,接着再用得到的商作为被除数去除以2,这次得到的余数就是次低位,如此循环,直到被除数为0为止。其实,只要明白了打印出一个十进制数的每一位的方式(不断除以10,得到的余数就分别是个位,十位,百位),就很容易理解十进制数转二进制数的这种方式。
1 | public class Test8 { |
28人买可乐喝,3个可乐瓶盖可以换一瓶可乐,那么要买多少瓶可乐,够28人喝?假如是50人,又需要买多少瓶可乐?(需写出分析思路)
由题设3个瓶盖可以换一瓶可乐知即3瓶可以换1瓶。
解:设需要买x瓶可乐够28人喝。
x/3 + x = 28
(4/3)x = 28
x = 28*3/4
x = 21(瓶)
答:要买21瓶可乐够28人喝。
解:设需要买x瓶可乐够50人喝。
x/3 + x = 50
(4/3)x = 50
x = 50*3/4
x = 37(瓶)...2(瓶盖)
答:买37瓶的时候还差2个瓶盖才够50人喝,所以需要买38瓶。
1 | public class Test9 { |
金额转换,阿拉伯数字转换成中国传统形式。例如:101000001010 转换为 壹仟零壹拾亿零壹仟零壹拾圆整
1 | public class Test10 { |