二、Java基础--02
作为本人首篇黑马技术博客有必要交代一下背景。个人理解博客的用作在于于己在于交流,于他在于学习,在交流学习中共同成长。下面进入正题。本文主要是介绍在做黑马入门测试时的一些问题(这个应该不是泄露题库吧)。
本文只要是讲述黑马程序员基础测试题的6-10题,回过头来分析明显感觉到难度的上升,考察的级别也有所提升,尤其是第9、第10,而且有一定的实用性,后文会进行分析。
第六题是关于面向对象的继承的基本知识,这个题属于大路边上的题,没有难度,这篇博客只给出原题,我会在后续的博客中进行更深入的分析。题目描述:
声明类Person,包含2个成员变量:name、age。定义函数sayHello(),调用时输出:我叫***,今年***岁了。声明类Chinese继承Person。
第七题是关于数组去重的,题目描述:
数组去重复,例如: 原始数组是{4,2,4,6,1,2,4,7,8},得到结果{4,2,6,1,7,8}
这个题其实没有什么难度,但是有一个特点是输出的结果不是按照排序之后输出而是只是将原来的数组中重复的提出,但是顺序不变。刚开始我的想法是先借助Arrays.sort(),将数组排序然后前后进行比较,如果后一个和前一个相同则不输出,否则就输出,虽然可以实现去重,但是顺序改变了,显然不满足题目要求,具体程序实现如下:
@Test
public void testMain()
{
int a[]={4,2,4,6,1,2,4,7,8};
Arrays.sort(a);//将数组从小到大进行排序
moveTheSame(a);//调用去重函数
}
private void moveTheSame(int[] a) {
for(int i=0;i<a.length-1;i++)
{
if(a[i]!=a[i+1])
System.out.print(a[i]);
}
if(a[a.length-2]!=a[a.length-1])//处理边界情况,
System.out.println(a[a.length-1]);
}
另外一种做法是通过Set集合元素不可重复的属性进行。具体实现如下:
@Test
public void testMain()
{
int a[]={4,2,4,6,1,2,4,7,8};
int b[]=moveTheSame(a);//数组去重
for(int i:b)//遍历输出
System.out.print(i);
}
private int[] moveTheSame(int[] a)
{
Set<Integer> mySet=new HashSet<Integer>();
for(int i=0;i<a.length;i++)//将元素放入Set集合
{
mySet.add(a[i]);
}
Object[] o=mySet.toArray();
int b[]=new int[o.length];
for(int i=0;i<b.length;i++)//为了保证与原数组类型一致,如果只是输出的话直接返回Object就行
b[i]=Integer.parseInt(o[i].toString());
return b;//返回原数组类型
}
还有一种做法是将数组转化为String,然后依次进行比较(和第一个相似),但是考虑到大于10的数,所以放弃,但是思想有借鉴价值,下面的结果就是借鉴这个原理得到的
public static void main(String[] args) {
int a[] = { 4, 2, 4, 6, 1, 2, 4, 7, 8 };
int b[] = moveTheSame(a);// 调用数组去重方法
for (int i = 0; i < b.length; i++)
// 按原格式输出
if (i != b.length - 1)
System.out.print(b[i] + ",");
else
System.out.print(b[i]);
}
private static int[] moveTheSame(int[] a) {
List<Integer> list = new ArrayList<Integer>();// 声明一个ArrayList,默认值为null
for (int i = 0; i < a.length; i++)
// 依次编译数组,如果list中不包含当前的元素则将当前元素加入到list,如果包含则忽略。
if (!list.toString().contains(a[i] + ""))
list.add(a[i]);
int b[] = new int[list.size()];
Object o[] = list.toArray();// 将list中元素转化为数组
for (int i = 0; i < b.length; i++)
// 同样是为了保证与原数组类型一直
b[i] = Integer.parseInt(o[i].toString());
return b;
}
上面介绍了三种方法,但是输出结果都是已排序和题目要求矛盾,上面介绍第三中方法,这种方法思想借助上面的分析,其实也是通过上面的分析得出的最终解决方案。我们在做一些算法题目往往不是一步就成功,而是将大问题分解为小问题一步步去实现。
总结:
1,观察数组特点得知数组的输出结果是一个无序数组,所以使用将数组转化为set集合的方案
(或者先借助Arrays.sort()进行排序,然后进行判断)行不通。
2,考虑将数组转化为Stirng类型,借助中间变量去判断中间变量中是否包含当前迭代元素,
如果包含则忽略,不包含则将当前元素加入到中间变量,考虑到大于10的数据,放弃本方案
3,直接借助List,如果中间变量List中存在当前变量则忽略,不存在则加入list中,
可以解决上面出现的问题。但是注意返回的类型应该保持与原来数组类型一致,
因此迭代将每个Integer元素转化为int,然后返回int数组
大家可以看到简简单单一道题有着这么大的工作量,当你写的多了对程序的设计就很快做出了,一步一步来才是王道。
第八题也是考察的只是是内部类已经其调用,这也是一个很重要的一个知识点,由于考察的形式简单,在此也就简单的分析。题目描述如下:
在打印语句中如何打印这3个x变量?
class A // 外部类
{
int x = 1;
class B// 内部类
{
int x = 2;// 内部类属性
void func() //内部类成员方法
{
int x = 3;
System.out.println(x);
}
}
}
参考解决方案:
A a=new A();//建立外部类的实例对象
System.out.println(a.x);//通过外部类的实例对象调用外部类的属性
A.B b=a.new B();//实例化内部类的对象
System.out.println(b.x);//通过内部类的实例对象调用内部类的属性
b.func();//通过内部类的对象调用内部类的成员方法
第九题也是我对我启发特别大的一个题目,是关于IO流的,之前学习过但是不是很熟悉,我的了解IO无非就是文件读写,也就是上传下载。在做JavaWeb项目时通常会用到工具包,所以对IO的重要性就淡化了好多,但是这道题给了我很多启发。题目描述如下:
编写程序,将指定目录下所有.java文件拷贝到另一个目的中,并将扩展名改为.txt
思路:
1.遍历指定目录的文件夹,并将以.java结尾的文件封装到list中,以便后续操作。
2.创建一个目的目录myDir
3.遍历list,指定文件目录为myDir,文件名为源文件名,后缀为.txt
程序实现如下:
public static void main(String[] args) throws IOException {
File dir = new File("F:\\a");
List<File> list = new ArrayList<File>();
file2List(dir, list);
list2TextbyChar(list);
}
// 遍历指定目录下的所有文件,并将*.java文件封装到List中
static void file2List(File dir, List<File> list) {
File files[] = dir.listFiles();
for (int i = 0; i < files.length; i++) {
if (files[i].isDirectory()) {// 如果当前文件为目录,递归遍历
file2List(files[i], list);
} else if (files[i].toString().endsWith(".java"))// 如果当前文件是以.java结尾,也就是java文件那么将该文件添加到list中
list.add(files[i]);
}
}
// 遍历封装了java文件的list,并将对应信息写到指定目录下对应的*.txt文件中
static void list2TextbyChar(List<File> list) throws IOException {
File f = new File("E:\\myDir");// 指定目标文件目录,如果不存在就新建该目录
f.mkdirs();
for (int i = 0; i < list.size(); i++) {
// 获取目标文件路径,已经源文件的文件名,将.java替换为.txt
String fileName = f+ "\\"+
list.get(i).getName().toString().substring(0,list.get(i).getName().toString().length() - 5)+ ".txt";
FileReader fr = new FileReader(list.get(i).getAbsoluteFile());// 读取每个文本文件的信息
FileWriter fw = new FileWriter(fileName);// 创建同名目录
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.close();
fr.close();
}
}
说这个题对我的启发大是因为有一个问题一直困扰着我,就是本人保存文件都是按照月份进行的但是时间长了去找文件就会很麻烦有时很长时间都找不到,很是纠结,在这个题目的启发下我将磁盘上所有文件,按照后缀名进行分类。这样对我来说带来了很大便利,比如我要找JDK的API,那么我只需去chm文件夹下找就ok了。下面程序演示我的实现:
public static void main(String[] args) throws IOException {
File dir=new File("E:\\");
List<File> list = new ArrayList<File>();
file2List(dir, list);
list2TextbyByte(list);
}
// 遍历指定目录下的所有文件,并将*.java文件封装到List中
static void file2List(File dir, List<File> list) {
File files[] = dir.listFiles();
int b = 0;
try {
for (int i = 0; i < files.length; i++) {
/*
if (!files[i].getAbsolutePath().contains("$")
&& !files[i].getAbsolutePath().contains("System")
&& !files[i].getAbsolutePath().contains("RECYCLER")
&& !files[i].getAbsolutePath().contains("Documents and Settings")
&&!files[i].getAbsolutePath().contains("Program"))*/
if (!files[i].isHidden())//这个是为了防止读取系统关键文件(会禁止访问而造成异常)
{
b = i;
if ( files[i].isDirectory()) {
file2List(files[i], list);
}
if (files[i].getAbsolutePath().endsWith(".chm"))
list.add(files[i]);
}
else
continue;
}
} catch (Exception e) {
System.out.println(files[b].getAbsolutePath());
e.printStackTrace();
}
}
static void list2TextbyByte(List<File> list) throws IOException {
File f = new File("C:\\mychm");// 知道目标文件目录,如果不存在就新建该目录
f.mkdirs();
for (int i = 0; i < list.size(); i++) {
// 获取目标文件路径,已经源文件的文件名
String fileName = f + "\\" + list.get(i).getName().toString();
FileInputStream fis = new FileInputStream(list.get(i)
.getAbsoluteFile());
FileOutputStream fos = new FileOutputStream(fileName);
int ch = 0;
byte by[] = new byte[1024 * 1024];
while ((ch = fis.read(by)) != -1) {
fos.write(by, 0, ch);
}
fos.close();
fis.close();
}
}
第十题是关于字节流的,在一份试卷中有两个题而且是压轴题是用到了IO,由此可见IO的重要性,题目描述如下:
编写函数,从一个字符串中按字节数截取一部分,但不能截取出半个中文(GBK码表)
例如:从"HM程序员"中截取2个字节是"HM",截取4个则是"HM程",截取3个字节也要是"HM"而不要出现半个中文
分析:在GBK下,一个英文字符为一个字节,一个汉字为两个字节。
处理时可以分情况进行处理:
1.读取字节时按照GBK的方式读取
2.用变量count控制读取的次数,当达到截取长度时break,结束程序
3.定义一个长度为1个字节的byte数组by[],用于处理单字节的内容,同时负责临时缓存半个中文的字符
4.定义一个长度为2个字节的byte数组by2[],勇于处理中文。
程序实现如下:
public static void main(String[] args) throws IOException {
mychar();
}
static void mychar() throws IOException {
String s = "HM程序员";// 初始化字符串
// 将字符串按字节(GBk)放在InputStream中
InputStream is = new ByteArrayInputStream(s.getBytes("GBK"));
// 定义需要截取字节的长度,以及给计数标记赋初值0,用以控制跳出条件
int n = 0, count = 0;
Scanner sc = new Scanner(System.in);
System.out.print("请输入截取字节长度:");
// 接收要截取的字节长度
n = sc.nextInt();
int len = 0;
// 用于处理长度为1的字符
byte[] by = new byte[1];
// 用于处理长度为2的字符
byte[] by2 = new byte[2];
int i = 0;
while ((len = is.read(by)) != -1)
{
if (!(by[0] < 0))// 字符
{
System.out.print((char) by[0]);// 将一个字节强转为字符并输出
} else if (i < 1)// 处理半个中文
{
i++;
by2[0] = by[0];
} else // 当i>0时表示处理汉子的低位字节,两个字节为一个汉字
{
by2[1] = by[0];
System.out.print(new String(by2));// 将分开的两个字节合成为一个中文字符并输出
by2 = new byte[2];
i = 0;
}
count++;
if (count == n)
break;
}
}
到此,试卷的讲解结束,谢谢您的阅读。