黑马程序员-重要代码记录Part1(end)


----------- android培训java培训、java学习型技术博客、期待与您交流! ------------

黑马程序员-重要代码记录

1.返回两个字符串的共有最大长度子字符串.

/*
需求分析:返回两个字符串的最大长度相同字符串.
思路:
1.录入两个字符串.
2.用循环以短的那个字符串长度n为循环次数.
3.再循环的n次中,建立内循环,循环从短的那个数组的0索引开始,长度从1到段数组的长度结束.定义新字符串.
4.进行判断该字符串是否在较长字符串中.
5返回循环结束后的那个新字符串.
*/

class MaxSubString
{
	public static void main(String[] args)
	{
		//录入两个字符串.
		String s1="awsxe3dcrfvtgbythnujmikd";
		String s2="gfdcrfxfgghgefs";
		System.out.println(getMaxSubString(s1,s2));
	}

	//定义方法,获取两个字符串的共有最大长度字符串.
	private static String getMaxSubString(String s1,String s2)
	{
		for (int x=0;x<s2.length();x++)//外循环定义用来辅助内循环定义新字符串的长度.
		{
			for (int y=0,z=s2.length()-x;z<s2.length();y++,z++)
				//通过对x的引用,控制y,z之间的字符串长度固定,这样,通过外循环控制长度避免索引越界和遍历不完全.
			{
				String temp=s2.substring(y,z);
				if (s1.contains(temp))
				{
				//	System.out.println(temp);
					return temp;
				}
			}
		}
		return "";
	}
}

总结:
最开始做这道题,总是在如何确定for循环的初始化条件和控制循环终点条件.
最后发现,还是得通过列举出实际情况,在实际情况中寻找规律.所以,关于那个思维记录贴中的第二篇Note还是相当有价值的.
因为是要截取共有最大长度子字符串.所以确定长度必然小于短的子字符串.
然后,用y,z(就是s2.length()-x),来定义每次内循环来截取字符串的首尾索引.相当于两个游标,游标之间长度固定,在短字符串中向末尾依次移位,看是否为相同子字符串.

---------------------------------------------------------------------------



2.控制台打印给定日期日历.

/*
需求:
通过键盘录入的年月日,打印出当月日历.
分析:
这里要用到1990,1,1日作为定位基点.某年某月某日减去1990,1,1日得到的天数模7的值即为星期几.
步骤:
1.键盘录入年月日.
2.通过for循环和if判断语句,累加天数.
3.通过辅助日期1990,1,1日得到该月第一天的星期数.
4.绘制日历表头,定位当月第一天在一周中的位置.
5.打印日历.
*/
import java.util.*;
class Date 
{
	public static void main(String[] args){
		//键盘录入年月日.
		System.out.println("\t\t日历查询\t\t");
		Scanner in=new Scanner(System.in);
		System.out.println("请输入年份:");
		int year=in.nextInt();
		System.out.println("请输入月份:");
		//int mouth=in.nextInt();
		  int mouth=in.nextInt();
		System.out.println("请输入日期:");
		int day=in.nextInt();
		System.out.println("您输入的是"+year+"年"+mouth+"月"+day+"日");
		int sum=0;
		//判断所输入年份,计算整年离1990年的天数
		for(int a=1990;a<year;a++){
			if(a%4==0&&a%100!=0||a%400==0){
				sum=sum+366;
				continue;
			}
				sum=sum+365;
	}
		//判断所输入月份,计算整月份离元月一号的天数
		for(int b=1;b<mouth;b++){
			if(b==2){
				if(year%4==0&&year%100!=0||year%400==0){
					sum=sum+29;
					continue;
				}else{
					sum=sum+28;
					continue;
				}
			}else if(b==4||b==6||b==9||b==11){
					sum=sum+30;
					continue;
				}
				sum=sum+31;
			}
	/*	//整年,整月天数,加上输入日期即是总共离1990年元月一日的天数
				sum=sum+day;
		System.out.println("这一天距离1990年,元月,一日总共"+sum+"天");
	}
	*/
		//绘制日历表头,定位当月第一天在一周中的位置.
		System.out.println("\t\t日历开始打印");
		System.out.println("日\t一\t二\t三\t四\t五\t六");
		//确定第一天,就是所选择日期月份第一天是周几,此时sum已经被注释过了,就是已经是单年份,月份,天数总和
		sum=sum+1;
		//进行月份的判断,将月份天数确定,赋值给m
		int m;
		if (mouth==2){
			if (year%4==0&&year%100!=0||year%400==0){
				m=29;
			}
				m=28;
		}else if (mouth==4||mouth==6||mouth==9||mouth==11){
				m=30;
		}else{
				m=31;
		}
		
		//此时的天数对7取余,余数是几,就是周几,并将1号定位并打印
		for (int i=1;i<=sum%7;i++){//将打印日历循环做出,循环M次
			System.out.print(" \t");
		}
		for (int n=1;n<=m;n++){
				if (sum%7==6){
				System.out.println(n+"\n");
				sum++;
				continue;
			}
				sum++;
				System.out.print(n+"\t");
		}
		System.out.println();
	}
}
总结:
学习java基础语法部分时,总是对循环有种莫名的恐惧 ,对于循环的控制条件难以把握.
日历打印题目是从网上看到的.拿来通过分析,思路,代码实现,相当有成就感.虽然现在回头发现很多需要改进,但是在入门期多做这样的联系还是相当有帮助的.
下面是后期学习了新知识的改进版(面向对象的思想渐渐深入,将方法尽量封装):
import java.util.Scanner;

/*
需求:建立一个简易的日历查询工具,输入需要查询的年月,在控制台打印该月日历(带星期).
分析:首先判断所输入日期的月份天数.在判断当月第一天星期数.再打印.
思路:
1.定义一个方法,给定年月日参数来打印日历.
2.键盘录入年月日,调用方法打印日历.
*/
public class DateSearch {
	public static void main(String[] args) {
		Scanner in=new Scanner(System.in);
		System.out.println("请输入想打印月份月历的年,月.\n请输入年份:");
		int year=in.nextInt();
		System.out.println("请输入月份:(1~12)");
		int month=in.nextInt();
		System.out.println("当前年份天数为:"+getYear(year));
		System.out.println("当前月份天数为:"+getMonth(month,year));
		printDate(year,month);
		
	}
	
	public static boolean ifRun(int year)
	{//建立方法,判断是否闰年.
		if ((year%4==0&&year%100!=0)||year%400==0) {
			return true;
		}
		return false;
	}
	
	//判断并返回当年天数.
	public static int getYear(int year)
	{
		return ifRun(year)?366:365;
	}
	
	//建立方法,判断并返回当月天数.
	public static int getMonth(int month,int year)
	{
		if (month==2) {
			if (ifRun(year)) {
				return 29;
			}
			return 28;
		}else if (month==4||month==6||month==9||month==11) {
					return 30;
		}else{
			return 31;
		}
	}
	
	//建立方法,返回需要判断天数的星期数(星期日为0)
	public static int getWeekday(int days)
	{
		return(days%7);
	}
	
	//定义方法,打印当月日历..
	public static void printDate(int year,int month)
	{
		int sum=0;
		for (int i = 1990; i < year ; i++) {//这里,因为1990,1月1日为周一,拿来做参照.
			if(ifRun(year)){
				sum+=366;
			}else{
			sum+=365;
			}
		}
		for (int i = 1; i <month; i++) {
			sum+=getMonth(i,year);
		}
		System.out.println("日\t一\t二\t三\t四\t五\t六\t\n-----------------------------------------");
		System.out.print(sum+"\n");
		for (int i=1;i<=sum%7;i++){
			System.out.print(" \t");
		}
		for (int n=1;n<=getMonth(month,year);n++){
				if (sum%7==6){
				System.out.print(n+"\n");
				sum++;
				continue;
			}
				sum++;
				System.out.print(n+"\t");
		}
	}
}

---------------------------------------------------------------------------

3.学习循环链表的建立与使用

链表类:(定义循环链表的方法类,包括创建,遍历,功能等)
/*
 		需求:学习循环链表的建立与使用
 		思路:
 				循环链变在于他每个对象内,有两部分组成,地址域和首地址.
 				每个元素的地址域指向链表中的他的下一个地址.而首地址则是他本个元素的对象的地址.
 		步骤:
 				1.建立第一个对象,对象内部定义下一个对象的指向.
 				2.建立构造函数,构造对象,如果为第一个对象,额外定义一个用来指向下一个对象的temp对象,用来保存需要传递的地址值.
 				3.如果是最后一个对象,则将其temp中的传递的地址值,指向第一个对象.				
 */
public class CycLink {
	//首先建立第一个成员的引用.就是将环形链表的头,定义.
	Member firstMember=null;
	Member temp=null;
	//定义成员数量.
	
	//测试类中使用的变量
	int num=0;
	int a=0;//play功能中,从那个结点开始.
	int b=0;//报数到b,此人踢出.
	
	CycLink(){}
	
	//设置环形链表的长度,就是结点的个数
	public void setNum(int num){
		this.num=num;
	}
	public void setA(int a){
		this.a=a;
	}
	public void setB(int b){
		this.b=b;
	}
	
	//初始化,建立循环链表.
	public void createLink(){
		for (int i = 1; i <= num; i++) {
			if (i==1) {
				//创建成员.
				Member m=new Member(i+"号");
				this.firstMember=m;
				temp=firstMember;
			}else if(i!=num){
				Member m=new Member(i+"号");
				temp.nextMember=m;
				temp=m;
			}else{
				Member m=new Member(i+"号");
				temp.nextMember=m;
				m.nextMember=firstMember;
			}
		}
	}
	
	//定义测试类中用到的约瑟夫问题具体使用
	public void play(){
		Member play =this.firstMember;
		System.out.println(play);
		for (int i = 1; i <a; i++) {
			play=play.nextMember;
			System.out.println(play);
		}
		while(play.nextMember!=play){
			for (int i = 1; i <b-1; i++){
					play=play.nextMember;
					System.out.println(play);
				}
				Member tempplay=play.nextMember;
//				System.out.println(play);
				//tempplay=tempplay.nextMember;
				play.nextMember=tempplay.nextMember;
				System.out.println("-----------------------");
				System.out.println(play+"\t踢掉报数为5的人."+tempplay.getName());
				System.out.println("-----------------------");
				play=play.nextMember;
				System.out.println(play);
			}
			System.out.println(play);
	}
	
	//遍历循环链表
	public void printCycLink(){
		Member start=this.firstMember;
		do{
				System.out.println(start);
				start=start.nextMember;
			}while(start!=firstMember);
		System.out.println("遍历结束,回到循环链表起始处");
	}
}
结点类:(封装每个结点包含的自身属性和指向属性)
public class Member {//环形链表中的元素,其实就是结点.应当包括其对象特有属性和一个结点特有的应用型指向,就是地址域.
	private String name;
	Member nextMember=null;
	Member(){}
	
	Member(String name){
		this.name=name;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString(){
		return name+"\t指向为:\t"+nextMember.name;
	}
}
测试类:(测试循环列表的创建,遍历增删是否正常,并可以结合具体问题实施)
public class CycLinkTest {
	public static void main(String[] args) {
		CycLink cy=new CycLink();//定义一个新的循环链表
		cy.setNum(20);//设置循环链表的长度.
		cy.createLink();//创建次循环链表.
		cy.setA(1);//设置开始结点.
		cy.setB(5);//设置报何数踢出.
		cy.play();
		//cy.printCycLink();//遍历打印循环链表.
	}
}
总结:
循环链表是在学习集合说到LinkedList集合底层是链表结构时想到的循环链表名词.结合之前做过的报数退出(约瑟夫问题),想到的高端实现方法.
循环链表的建立我发现其实再使用上对于现有的集合而言,有自己的独特优势:
在某些需要实时跟新集合,反馈集合信息进行后续执行的情况下,循环链表具有先天优势:增删快.
而他的弊端则在,循环链表需要人为定义初始结点,查找指定元素相对于其他所有集合都没有优势,必须从初始结点一个个向下找.
下面是关于约瑟夫问题我用循环的解答:(相对于用循环链表解答繁琐和很多.)
/*
有n个人围成一圈,顺序排号。从第一个人开始报数(从1到m报数),凡报到m的人退出圈子,问最后留下的是原来第几号的那位。求算法思路 
 */

import java.util.Scanner;

public class YueSeFu {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		System.out.println("请输入人数n和数到m出列的n,m值:");
		System.out.println("请输入n(大于0的整数):");
		int n = in.nextInt();
		System.out.println("请输入m(大于0的整数):");
		int m = in.nextInt();
		System.out.println("第" + getEnds(n, m) + "个人为最后留下来的人.");
	}

	public static int getEnds(int n, int m) {
		int[] arr = new int[n];// 建立一个长度为n的int型数组,元素为每个人的编号(标号为数组索引+1)
		for (int i = 1; i <= arr.length; i++) {
			arr[i - 1] = i;
		}
		
		int cycle = 0, cut = 0, i = 0;// 声明两个辅助循环的增量,cycle为循环数;cut为提出圈人数.
		for (i = 1; i <= arr.length; i++) {
			if (arr[i - 1] != 0) {
				cycle++;
			}
			
			if (cycle == m) {
				arr[i - 1] = 0;
				cycle = 0;
				cut++;
			}
			if (cut <= arr.length - 1){ // 判断条件,因为cut为最终终止循环的增量,所以先判断
				if (i == arr.length){// 判断为不终止循环的基础上,判断是否为循环最后一个数据,是,则给i赋值0,使之i++后重新为1,进入新一轮循环.
					i = 0;
					continue;
				}
			} else {
				break;
			}
		}		
		return i;
	}
}

----------------------------------------------------------------------

4.蛇形数组(相当锻炼思维)

/*
 * 需求:
 * 写一个方法,打印等长的二维数组,要求从1开始的自然数由方阵的最外圈向内螺旋方式的顺序排列.如:n=4,打印:
 * 				1		2		3		4						
 * 				12		13		14		5						
 * 				11		16		15		6						
 * 				10		9		8		7						
 * 思路分析:	
 * 		1.观察,可知需求的二维数组中元素应该对应所打印每个数据.行,及n为二维数组的长度及其中每个一位int型一维数组的长度.
 * 		2.同时,观察得到具体每个int型元素是由行,列边界值完全遍历,再到边界值-1完全遍历...直到填满整个二维数组.
 * 实现步骤:
 * 		1.建立一个每个int型一维数组长度为n,二维数组长度也为n的二维数组.
 * 		2.遍历赋值.
 * 		3.打印.
 */
public class SnakeArray {
	public static void main(String[] args) {
		printArray(3);
		System.out.println("\n------------分-----割-----线------------\n\n");
		printArray(7);
	}
	
	//定义一个按照需求打印二维数组的方法,参数为int型n.
	public static void printArray(int n)
	{
		int[][] arr=new int[n][n];
		int x=1;
		//arr[0][0]
		for(int p=0;p<n/2;p++){
				//arr[0][0]>>arr[0][n-2]
				for (int i = p; i < n-1-p; i++) {
					arr[p][i]=x++;
				}
				//arr[0][n-1]>>arr[n-2][n-1]
				for (int j = p; j < n-1-p;j++) {
					arr[j][n-1-p]=x++;
				}
				//arr[n-1][n-1]>>arr[n-1][1]
				for(int i=n-1-p;i>p;i--){
					arr[n-1-p][i]=x++;
				}
				//arr[n-1][0]>>arr[1][0]
				for(int j=n-1-p;j>p;j--){
					arr[j][p]=x++;
				}
				//arr[1][0]>>arr[1][1]之后循环上述过程,即可完成偶数n的赋值.
			}
		//若n为奇数,则需要对忽略位置[n/2][n/2]进行赋值.
		if (n%2!=0) {
			arr[n/2][n/2]=x;
		}
	
		//打印数组.
		for (int i = 0; i < n; i++) {
			for(int j=0;j<n;j++){
				System.out.print("\t"+arr[i][j]+"\t");
			}
			System.out.println("\n---------------------------------------------\n" );
		}
	}
}
总结:
通过这次分析蛇形数组,虽然用循环做出了最终答案,但是后来发现这明显用到了递归的思想.所以等面试落定,我再用递归方法来改进下.

----------------------------------------------------------------

5.控制台五子棋代码.

import java.util.Random;
import java.util.Scanner;

public class WuZiQi {
	static Scanner in = new Scanner(System.in);

	public static void main(String[] args) {
		printBoard();
	}

	// 选手落子.
	public static void printBoard() {
		String[][] board = new String[10][10];
		for (int i = 0; i < board.length; i++) {
			for (int j = 0; j < board[i].length; j++) {
				board[i][j] = " + ";
				System.out.print(board[i][j]);
			}
			System.out.println();
		}
		A: while (true) {// 判断若输入点已有棋子,则重新输入.
			int x, y;
			while (true) {
				System.out.println("请落子:(输入落子坐标值1~10)");
				x = in.nextInt() - 1;
				y = in.nextInt() - 1;
				if (ifRight(x) && ifRight(y))
					break;
				System.out.println("输入的棋子位置异常,请重新输入.");
			}
			if (board[x][y].equals(" ● ") || board[x][y].equals(" O ")) {
				System.out.println("此处已有棋子,请重新落子:");
				continue;
			} else {
				board[x][y] = " ● ";
			}
			/*
			 * for (int i = 0; i < board.length; i++) {// 打印棋盘. for (int j = 0;
			 * j < board[i].length; j++) { System.out.print(board[i][j]); }
			 * System.out.println(); }
			 */
			print(board);

			int count = 0;
			while (count != 1) {
				System.out.println("电脑落子,请稍等...");
				Random r1 = new Random();
				int xC = r1.nextInt(9) + 1;
				int yC = r1.nextInt(9) + 1;
				/*
				 * int xC = new Random().nextInt(9) + 1; int yC = new
				 * Random().nextInt(9) + 1;
				 */

				// int xC =(int)Math.random()*10+1;
				// int yC = (int) Math.random() * 10 + 1;
				// //为什么用Math里的random不行???
				if (board[xC][yC].equals(" O ") || board[xC][yC].equals(" ● ")) {
					System.out.println("此处已有棋子,请重新落子:");
					continue;
				} else {
					board[xC][yC] = " O ";
					count = 1;
				}
				/*
				 * for (int i = 0; i < board.length; i++) {// 打印棋盘. for (int j =
				 * 0; j < board[i].length; j++) { System.out.print(board[i][j]);
				 * } System.out.println(); }
				 */
				print(board);
			}

			// 判断输赢条件.
			String resoult1 = "";
			String resoult2 = "";
			String resoult3 = "";
			String resoult4 = "";
			for (int i = 0; i < board.length; i++) {// 判断横行,竖行出现五连.
				for (int j = 0; j < board[i].length; j++) {
					resoult1 += board[i][j];
					resoult2 += board[j][i];
				}
				if (resoult1.contains(" ●  ●  ●  ●  ● ")
						|| resoult2.contains(" ●  ●  ●  ●  ● ")) {
					System.out.println(" ● 选手赢了");
					break A;
				} else if (resoult1.contains(" O  O  O  O  O ")
						|| resoult2.contains(" O  O  O  O  O ")) {
					System.out.println(" O 选手赢了");
					break A;
				} else {
					resoult1 = "";
					resoult2 = "";
				}
			}
			/*
			 * for (int i = 0, j = 0; i <= 4 && j <= 4; i++, j++) { resoult3 +=
			 * board[i][j]; }
			 */
			for (int i = 0; i <= 4; i++) {// 判断右斜是否出现五连
				int a = i;
				for (int j = 0; j <= 4; j++) {
					int b = j;
					while (true) {
						resoult3 += board[a][b];
						resoult4 += board[b][a];
						a++;
						b++;
						if (a == 9 || b == 9) {
							a = i;
							b = j;
							break;
						}
					}
					if (resoult3.contains(" ●  ●  ●  ●  ● ")
							|| resoult4.contains(" ●  ●  ●  ●  ● ")) {
						System.out.println(" ● 选手赢了");
						break A;
					} else if (resoult3.contains(" O  O  O  O  O ")
							|| resoult4.contains(" O  O  O  O  O ")) {
						System.out.println(" O 选手赢了");
						break A;
					} else {
						resoult3 = "";
						resoult4 = "";
					}

				}
			}
			for (int i = 0; i <= 4; i++) {// 判断左斜是否出现五连
				int a = i;
				for (int j = 9; j > 4; j--) {
					int b = j;
					while (true) {
						resoult3 += board[a][b];
						resoult4 += board[9 - a][b];
						a++;
						b--;
						if (a == 9 || b == 0) {
							a = i;
							b = j;
							break;
						}
					}
					if (resoult3.contains(" ●  ●  ●  ●  ● ")
							|| resoult4.contains(" ●  ●  ●  ●  ● ")) {
						System.out.println(" ● 选手赢了");
						break A;
					} else if (resoult3.contains(" O  O  O  O  O ")
							|| resoult4.contains(" O  O  O  O  O ")) {
						System.out.println(" O 选手赢了");
						break A;
					} else {
						resoult3 = "";
						resoult4 = "";
					}
				}
			}
		}
	}

	private static boolean ifRight(int x) {// 判断用户输入的数据转化成的数组索引是否合理
		return (x >= 0 && x <= 9) ? true : false;
	}

	public static void print(String[][] board) {
		// System.out.println( "  1  2  3  4  5  6  7  8  9  ⑩");
		for (int i = 0; i < board.length; i++) {// 打印棋盘.
			for (int j = 0; j < board[i].length; j++) {
				System.out.print(board[i][j]);
			}
			System.out.println();
		}
	}

}



总结:
写这个代码的最初原因是在学习疯狂JAVA书的时候,看他了作者在书中提到的用二维数组可以实现在控制台上布置五子棋棋盘.
然后便突然想用控制台做出一个可以跟电脑一起下五子棋的控制台程序.
现在存在的问题:电脑没有落子的优先级.(思考后,认为可以这样,定义一个枚举类,元素为可能出现的2,3,4连的棋子情况,判断当前棋盘是否存在枚举中元素,有则在其首位落子.无则先现在实现的那样,random落子)

---------------------------------------------------------------------

5.表象文件隐藏其他文件


/*
 需求:
 通过复制的追加模式,将两个文件中的指定文件用另一文件隐藏.
 通过反向读取将上述方法的隐藏文件与表象文件分离.
 思路:
 文件的复制有追加模式,而追加模式如果在一个已经具有文件格式的文件上进行,则会在不改变该文件表面属性
 的基础上,将需要隐藏文件的数据追加在其后.
 对于一个用某文件隐藏了目标文件的文件,通过从去表面文件结尾开始截取可以进行还原.
 步骤:
 1.给定两个文件,表象文件(最好为图片):用来确定隐藏信息文件的表现形式.指定隐藏文件:需要在表象文件下隐藏的文件.
 2.定义方法,先创建表象文件副本,而后在副本以追加模式写入需隐藏文件.
 3.定义方法,将隐藏信息文件进行分解,需要用表象文件来辅助定位开始截取位置.
 4.保存截取后文件(即原带隐藏文件).
 */

import java.io.*;
import java.util.Scanner;

class CoverInfo {
	public static void main(String[] args) throws IOException {
		Scanner in = new Scanner(System.in);
		System.out.println("请输入隐藏文件所用表象文件路径:");
		String outerStr = in.nextLine();
		System.out.println("请输入隐藏文件路径:");
		String innerStr = in.nextLine();
		System.out.println("请输入隐藏文件完成后存储路径:");
		String targetStr = in.nextLine();
		File outer = new File(outerStr);
		File inner = new File(innerStr);
		File target = new File(targetStr);	
		coverInfo(outer, inner, target);
	}

	// 定义隐藏文件方法.
	private static void coverInfo(File outer, File inner, File target)
			throws IOException {
		if (!outer.isFile() || !inner.isFile()) {
			System.out.println("文件不存在,请检查.");
		}
		if (!target.exists()) {
			System.out.println("目标目录不存在,自动创建.");
			target.mkdirs();
		}
		BufferedInputStream bisOut = new BufferedInputStream(
				new FileInputStream(outer));
		BufferedInputStream bisIn = new BufferedInputStream(
				new FileInputStream(inner));
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(new File(target, outer.getName())));
		byte[] temp = new byte[1024];
		int len = 0;
		while ((len = bisOut.read(temp)) != -1) {
			bos.write(temp, 0, len);
		}
		bisOut.close();
		while ((len = bisIn.read(temp)) != -1) {
			bos.write(temp, 0, len);
		}
		bos.close();
		bisIn.close();

		System.out.println("隐藏文件操作成功.");
	}
}
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;
//定义抽取隐藏文件类
class ReCoverInfo {
	public static void main(String[] args) throws IOException {
		Scanner in = new Scanner(System.in);
		System.out.println("请输入隐藏文件所用表象文件路径:");
		String outerStr = in.nextLine();
		System.out.println("请输入隐藏文件操作后文件路径:");
		String coveredStr = in.nextLine();
		System.out.println("请输入隐藏文件提取后存储路径:");
		String targetStr = in.nextLine();
		File outer = new File(outerStr);
		File coveredFile = new File(coveredStr);
		File target = new File(targetStr);
		reCoverInfo(outer, coveredFile, target);
	}

	// 定义反向获取隐藏文件方法
	private static void reCoverInfo(File outer, File coveredFile, File target)
			throws IOException {
		if (!outer.exists() || !coveredFile.exists()) {
			System.out.println("文件不存在,请检查.");
		}
		if (!target.exists()) {
			System.out.println("目标目录不存在,自动创建.");
			target.mkdirs();
		}
		BufferedInputStream bisOut = new BufferedInputStream(
				new FileInputStream(outer));
		BufferedInputStream bisCoveredFile = new BufferedInputStream(
				new FileInputStream(coveredFile));
		target = new File(target, "提取出的隐藏文件_请自加后缀");
		BufferedOutputStream bosReCoverInfo = new BufferedOutputStream(
				new FileOutputStream(target));
		byte[] temp = new byte[1024];
		int len = 0;
		while ((len = bisOut.read(temp)) != -1) {
			bisCoveredFile.read(temp);
		}
		while ((len = bisCoveredFile.read(temp)) != -1) {
			bosReCoverInfo.write(temp, 0, len);
		}
		bosReCoverInfo.close();
		bisCoveredFile.close();
		bisOut.close();

		System.out.println("隐藏文件提取操作成功.");
	}
}
总结:
这个小代码说起来相当简单,其实就是在建立输出流对象是,将构造函数改为(File File,boolean true),即为追加模式.
在学习使用此代码时,发现追加模式写入另外文件信息时,并没有破坏第一个文件的数据结构,所以在window中能表现出表象文件的属性,也可以正常使用.但是同样的,在后面存储的完整的第二个文件的数据也得到了完美保存 ,将至截取出来重新封装便又还原了那个隐藏的文件.
虽然代码没什么技术含量,不过记录下,算是一个小小的发现...

---------------------------------------------------------------------

6.关于Map的keySet()方法返回的Set类型的探究.

/*
需求:获取一个字符串中出现的字母及其出现次数。
分析:字符串每位上的字符进行遍历,然后将每位上的字符添加入定义好的一个Map集合。存储动作时加上判断,如果集合中不存在该键(字符作键),添加该键值(字符,1),如果存在则值加1.
步骤:
1.键盘获取一个字符串对象.
2.定义一个Map,遍历字符串,将每一位上的字符当作键,存入Map集合。
3.存储都做加上判断,集合中没有,则值为1,存入。集合中有,则将已有值加一存入。
4.遍历Map集合并打印,达到要求格式。
*/
import java.util.Map;
import java.util.TreeMap;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;
class ApearNum
{
	public static void main(String[] args)
	{
		Scanner in = new Scanner(System.in);
		System.out.println("请输入需要获取出现字母及其次数的字符串:");
		String str = in.nextLine();
		TreeMap <Character,Integer> strMap = new TreeMap<>();
		for(int i=0;i<str.length();i++)		//这里,突然想到增强for,适用于集合和数组,但不论从哪方面来说,String我都觉得他足够具有特殊性。
		//for(Character str.charAt(i) : str)		//所以这里,对增强for是否适用于String,进行了实验,事实证明,不行
		//for(Character charI : str)
		{
			//Character temp =charI;
			Character temp=str.charAt(i);//遍历字符串.给自定义Map添加元素.
			if(strMap.get(temp)==null)//如果Map中不存在,
			{
				strMap.put(temp,1);
			}
			else
			{
				strMap.put(temp,strMap.get(temp)+1);
			}
		}
		System.out.println(strMap);
		//TreeSet <Character> strSet = (TreeSet)strMap.keySet();   这里,运行出现类型转换异常,可以得知,keySet()得到的键集合是与Map对应的类型.
		//TreeSet <Character> strSet =(TreeSet)strMap.keySet();	为什么这里,我已经将Map改成TreeMap还是类型转换异常?
		Set <Character>	strSet = strMap.keySet();
		TreeSet strSet2 = (TreeSet)strSet;
		StringBuilder strSB = new StringBuilder();
		for(Character key : strSet)
		{
			strSB.append("[").append(key+":").append(strMap.get(key)).append("] ");
		}
		System.out.println(strSB);
	}
}

总结:

在上面的代码中,在我进行Map集合遍历的时候,有了这种想法,就是keySet返回的应到是key的一个Set集合,而众所周知,Map的具体子实现类的key是通过特定方式保证唯一的,就是Tree(二叉树)Hash(哈希表).

所以我就想这里得到的keySet()返回的Set集合是不是应该就是对应的TreeSet或者HashSet集合.

所以,代码中我用强制类型转换进行了试验,结果是悲剧的...他一个都不是.

所以从源代码中寻找答案.

发现,原来,TreeMap中的keySet()方法中,定义了一个静态的成员内部类,实现了一个叫SortedSet的接口(TreeSet也实现了该接口).并不是我想想的直接是TreeSet或者HashSet.

实际上TreeMap中和HashMap中该方法返回的分别都是静态的成员内部类,继承AbstractSet.

所以,这里我不得不感慨,Java多态思想运用的太太太出神入化了.

就像现实世界一句经典的话:不管黑猫白猫.抓到老鼠,都是好猫!

---------------------------------------------------------------------------


----------- android培训java培训、java学习型技术博客、期待与您交流! ------------
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值