搜档网
当前位置:搜档网 › Java基础经典练习题

Java基础经典练习题

一:

/**
* 1、 取出一个字符串中字母出现的次数。如:字符串:"abcdekka27qoq" ,
* 输出格式为:a(2)b(1)k(2)...
*
* 思路:
* 1.需要知道每个字符出现的次数,就要对每个字符进行遍历.
* 2.如何对遍历出来的字符和对应出现的次数进行存储呢?就需要map集合.
* 3.这里一个问题就是,如何把每个字符出现的最完美的次数装入map中呢?
* 就需要通过一个判断,如果这个字符不存在,就将key以及对于value存入.
* (如果已经存在,说明这个字符遍历的已经不是最大的value值了)
* 4.当这个map集合把元素都存入之后,再通过EntrySet()将这个关系对遍历出来.然后再分别获取key和value.最后打印.
*
* 总结:
* 1.对于出现了有映射或者对应关系的元素时,必须想到要用到map集合.
* 2.对于要查询出现的次数,必须要能够想到需求遍历.
*
* 思考:
* 字符串和数组之间就如同亲兄弟一样.而map和set之间也如同亲姐妹一般.
* 字符串依靠数组来进行元素遍历,map通过set集合来将元素遍历.
* 所以,对于玩法操作中涉及到字符串的,需要想到他的兄弟数组.涉及到map集合的,需要想到它的亲姐妹set集合.
* */
public class Demo01 {
public static void main(String[] args) {
String s = "fdskai2353425hfdsakhfdska";
Map map = StringList(s); //接受一个字符串,返回一个map集合.
ListMap(map); //接受一个map集合,遍历并打印.
}

public static void ListMap(Map map) {//遍历map
Set se = new HashSet();
se = map.entrySet();
Iterator it = se.iterator();
while(it.hasNext())
{
Entry en = (Entry)it.next();
System.out.print(en.getKey() + "(" + en.getValue() + ")");
}
}

public static Map StringList(String s) { //将字符串遍历
char[] ch = s.toCharArray();
Map map = new HashMap();
for(int x = 0; x < ch.length ; x ++)
{
int a = 0; //定义一个变量,每次都是从x角标在y循环中获取次数.
for(int y = 0; y < ch.length; y ++)
{
if(ch[x]== ch[y])
{
a ++; //如果相同了,就让变量加一.
}
}
if(!map.containsKey(ch[x])) //如果不存在才装进来.保证这个value是最大的次数值.
{
map.put(ch[x], a);
}
}
return map;
}
}

二:
/**
* 2、 假如我们在开发一个系统时需要对员工进行建模,员工包含 3 个属性:
* 姓名、工号以及工资。经理也是员工,除了含有员工的属性外,另为还有一个奖金属性。请使用继承的思想设计出员工类和经理类。要求类中提供必要的方法进行属性访问。

思路:
1.首先确定,谁是父类?谁是子类?
2.父类已经拥有的,子类通过super就可以获取到,对于父类来说,

提供最基本的
属性以及方法.对于子类来说,既然是继承,必须要有自己独特的方法和属性才存在意义.

总结:
面向对象的一个特征----继承.很大程度上减少了代码量,增加了程序的功能扩展,并提高代码的复用.

思考:
继承既然从某种程度上增加的程序的功能扩展以及代码复用.但是,万事之间从来都是利弊交替.
那么究竟继承会存在什么样的弊端呢?
1.由于继承只可以是单继承,那么如果该子类想要重新定义一个体系功能时,就没有再可以继承的权利了.
2.继承也让之前单一的程序变成连续的整体体系.从维护角度看,相对来说也增加了维护的难度.
3.据以以上,因此继承需要在一个必须拥有强大共性的基础上继承才会带来更多效益,为继承而继承,就会使得程序变得轻浮,更加难以维护.

* */
public class Demo02 {
public static void main(String[] args) {
Enployee en = new Enployee("王武",1000,3653.33); //员工父类
Manager ma = new Manager("赵六", 199, 5988.22, 3000.11); //经理子类.

System.out.println(en.toString());
System.out.println(ma.toString());
}
}
class Manager extends Enployee
{
private double bonus;
Manager(String name,int id,double pay,double bonus)
{
super(name,id,pay); //既然父类有了,我直接用还不行吗?
this.bonus = bonus; //父类没有的,那只能自己想办法了...
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public String toString() { //复写Object中toString方法
return "Manager [bonus=" + bonus + ", Name =" + getName()
+ ", Id =" + getId() + ", Pay =" + getPay() + "]";
}

}

class Enployee //定义父类 员工类
{
private String name; //父类中基本的属性.
private int id;
private double pay;
Enployee(String name,int id,double pay) //初始化参数.
{
https://www.sodocs.net/doc/d716944091.html, = name;
this.id = id;
this.pay = pay;
}
public String getName() { //必要的set,get方法.
return name;
}
public void setName(String name) {
https://www.sodocs.net/doc/d716944091.html, = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getPay() {
return pay;
}
public void setPay(double pay) {
this.pay = pay;
}
@Override
public String toString() { //复写的toString方法.
return "Enployee [name=" + name + ", id=" + id + ", pay=" + pay + "]";
}
}



三:

/**
* 3、 ArrayList list = new ArrayList();
在这个泛型为Integer的ArrayList中存放一个String类型的对象。

思路:
1.给泛型为Integer的ArrayList中添加一个String类型对象,为什么会
添加不进去?
2.是因为ArrayList只接受Integer类型,那么,我让它不只是接受Integer
不就可以了吗?
3.如

何让它不是只接受Integer呢?既然是接收,靠什么接受?自然是ArrayList中的add方法了.
4.既然是一个方法,我通过反射将方法接受类型改变,不就O了吗?
5.想反射,首先要对象字节码对象,想获取方法,还需要一个ArrayList对象.还好,对象已经有了...

总结:
1.所谓的泛型,也就是在方法上对于接受的类型进行了一个限定.
2.反射方法和字段以及构造,字节码就是必须的.运行方法,对象也是必要的.
只是构造本来就是new对象的,而获取字段和方法需要有字节码对象,运行,就要有对象参与.
3.对于反射,一定需求注意的是反射的对象所被修饰的修饰符是什么?如果是public修饰的,直接可以getMethod()这种形式就可以,如果是默认的,就需要通过getDeclaredMethod()才可以,要是private的话,对不起,必须要通过暴力反射了,就是要通过getDeclaredMethod(),并设置me.setAccessible(true)来进行设置权限.

思考:
1.泛型的出现阻止了形形色色的元素混入,可是,有了反射,就好比让接受重新回到解放前.
2.反射的确是让程序重新变得不安全,因此,对于特别修饰的private,我们需求慎重来考虑一下,毕竟private自然有private的道理.

* */
public class Demo03 {
public static void main(String[] args) throws Exception {
ArrayList al = new ArrayList();
Class clazz = ArrayList.class; //获取字节码对象.
Method me = clazz.getMethod("add", Object.class); //反射方法.
me.invoke(al,"我是字符串"); //运行方法.
System.out.println(al);
}
}

四:

/**
* 4、 有五个学生,每个学生有3门课(语文、数学、英语)的成绩,
* 写一个程序接收从键盘输入学生的信息, 输入格式为:name,30,30,30
* (姓名,三门课成绩), 然后把输入的学生信息按总分从高到低的顺序写入
* 到一个名称"stu.txt"文件中。要求:stu.txt文件的格式要比较直观,
* 打开这个文件,就可以很清楚的看到学生的信息。
*
* 思路:
* 1.学生的信息要从键盘接受,那么怎么样能把从键盘接受的数据变成
* 一个学生的属性信息呢?接受的是一个字符串,切割行不?
* 2.切割的字符串,变成字符串数组,由于输入的信息是有规律的,那么
* 就可以根据角标对应的学生信息进行传入.
* 3.学生信息传给了学生对象了,那么对象要怎么做呢?学生对象,需要
* 对这些信息进行整理.那么学生需要有对于这个信息有必要的判断方法.比如是不是同一个人呢?
* 4.一个一个的学生对象,我要怎么样整理呢?必须是集合了,集合就是来整理
* 对象的.学生对象都放集合了,还要对成绩有一个排序?那必须要集合自身的比较性才 可以完成.(学生只能自己和别人比一下,因为如果将学生

作为一个对象参考的话,只知道自己和别人是不是一样的.而如果要站在学生整体上的排序,眼光就要站在一个可以统领和掌控学生的对象,也就是集合.)
* 5.集合,遍历,再打印学生对象信息,再输入到指定文件.
*
* 总结:
* 1.一个对象中可能存在很多属性,那么这些属性之间存在什么样的关系呢?有些属性是直接可以获取的,有些属性,是建立在其他属性的基础上才存在意义的.
* 2.学生如何比较,实现的是Comparable接口,重写了CompareTo方法.而集合比较是通过Comparator接口,实现compare方法.也可以一个自定义比较器.而这个比较器还是要通过comparator这张脸让集合自身具备了比较性.因此,comparator可以看作是集合比较方法一个中介.
* 3.集合自身比较性和元素自身比较性之间存在什么联系呢?好乱的感觉.我个人觉得,集合是一个更大的存在,但是这个集合要比较小小的元素,如何知道元素是排前还是排后呢?根据什么呢?就是根据元素给集合"提供的信息",元素本身做为一个对象,只知道自己和另一个元素谁大谁小,但是由于视角的局限性,让整体元素无法做出一个排序,只能把信息传递给集合,而集合可以站在一个更高的角度来说:你应该站在哪里.
* (好比a小朋友和b小朋友比较成绩,他们两个很清楚,但是他们无法做出整个班级小朋友的比较.这个时候小朋友需要把信息提供给老师,然后老师才能站在更高角度来进行审视,谁应该排在哪里....小朋友就相当于学生对象,老师就相当于集合)
*
* 思考:
* 1.对于编程而言,尤其是java的编程思想.对象和对象之间如何产生关系.既然万物都是对象,想必然对象和对象之间也会存在视角限制.这里面存在一个层次问题,我觉得,这个层次在心里一定要清晰可见.比如我站在一个教室的角度去看对象,那么就有了桌子,空调...等等.如果我站在一个桌子的角度,那么桌子上的水杯,书本...等等才会进入我的视角.不然的话,站在一个教室的角度,去审视一个水杯,这就属于跨级审视.结果就是混乱了.
* 2.对于每一个功能体,尽量做到独立封装.哪些是我需要控制的,就放外面,哪些是一个功能体,就封装起来.比如,我要控制存放的文件地址,那么,我就创建一个文件地址放在外面.我对排序要求控制,就需要在传入参数时候让其具备灵活性.假如这个功能是接受一个文件,返回一个字符串,那么就尽量封装起来..对于以后如果有相同需求,我们还可以调用以进行功能复用.
* 虽然只是一些小小的程序.但是,我们有必要从开始就拥有一个较完美的编程思想.等到程序变得越来越复杂的时候,我们也可以轻而易举的掌控.
* */
public class Demo04 {
public static void main(String[] args) throw

s IOException {
Comparator com = Collections.reverseOrder(); //自定义一个比较器
File fi = new File("c:\\stu.txt"); //指定文件目录.定义在外面更好一些,提高扩展.
Set se = StudnetIn(com,false); //将比较器传入方法以及排序方式..返回一个集合.
ToFile(se,fi); //将集合和file传入.
}

//对于集合进行遍历,获取对象元素.并输出到指定文件.
public static void ToFile(Set se,File fi) throws IOException {
Iterator it = se.iterator();
FileWriter fw = new FileWriter(fi);
while(it.hasNext())
{
Student s = it.next();
fw.write(s.toString() + s.getMax() + "\t"+"\r\n");
}
fw.close();
}

//传入比较器以及排序需求.如果是true,就是默认排序,否则就是自定义的排序.
public static Set StudnetIn(Comparator com,boolean bo) {
Scanner s = new Scanner(System.in);
Set se = null;
if(bo == true) //自然排序.
{
se = new TreeSet();
}
else //自定义排序(也就是定义的倒序)
{
se = new TreeSet(com);
}
while(true)
{
String ss = s.nextLine();
if(ss.equals("over")) //输入结束指令.
{
System.out.println("输入结束");
break;
}else
{
//切割,并将字符串和角标传递给student对象,并存入集合中.
String[] str = ss.split(",");
se.add(new Student(str[0],Double.parseDouble(str[1]),
Double.parseDouble(str[2]),Double.parseDouble(str[3])));
}
}
return se;
}
}

//定义一个实现Comparable的学生对象.
class Student implements Comparable
{
private String name;
private Double ch;
private Double ma;
private Double en;
private Double max;
Student(String name,Double ch,Double ma,Double en) //参数直接收语文,数学,英语,为什么?
{
https://www.sodocs.net/doc/d716944091.html, = name;
this.ch = ch;
this.ma = ma ;
this.en = en;
this.max = ch + ma + en; //总成绩是算出来的,不是传入的.
}
public int compareTo(Student stu) //重写compareTo方法.
{
//在这里要注意:谁是关键的?我们要比较什么?----成绩是关键的.
int i = new Double(this.max).compareTo(new Double(stu.max));
if(i == 0) //只有成绩相等,再去比较名字.
return https://www.sodocs.net/doc/d716944091.html,pareTo(https://www.sodocs.net/doc/d716944091.html,);
return i;
}
public int hashCode() //重写hashCode方法.
{
return (int) (https://www.sodocs.net/doc/d716944091.html,.hashCode() + this.max * 66);
}
public boolean equals(Student stu) //重写equals方法.
{
return https://www.sodocs.net/doc/d716944091.html,.equals(https://www.sodocs.net/doc/d716944091.html,) && this.max == stu.max;
}

public String getName() {
return name;
}
public void setName(String name) {
https://www.sodocs.net/doc/d716944091.html, = name;
}
public Double getCh() {
return ch;
}
public void setCh(Double ch) {
this.ch = ch;
}
public Double getMa() {
return ma;
}
public void setMa(Double ma) {
this.ma = ma;
}
public Double getEn() {
return en;
}


public void setEn(Double en) {
this.en = en;
}
public Double getMax() { //只想对外提供总成绩的get,而不提供set,为什么?
return max;
}
@Override
public String toString() {
return "Student [name=" + name + ", ch=" + ch + ", ma=" + ma + ", en="
+ en + "]";
}
}


五:

/**
* 定义一个文件输入流,调用read(byte[] b)方法将exercise.txt
* 文件中的所有内容打印出来(byte数组的大小限制为5)。
*
* 思路:
* 1.要求是放入byte[]中,那么指定就是字节流了.
* 2.读的方法是read(byte[] b),可是byte只有5,我数据够长,
* 5是明显不够的,那么怎么办?
* 3.将5个单位5个单位的数据先放一块,最后再一次性打印OK不?
* 4.谁可以将这些数据先保存呢?内存输出流,它并没用真的将数据写出去,
而是先全部留下来,等没有数据了,再通过ToString()将数据写出去.

总结:
1.还是那句话,是功能就尽量封装,什么都不重要,思想和习惯最重要.
2.字节流就是对应字节数组.
思考:
在IO流中,内存流就相当于StringBuilder的用法.先把数据都放入进来,
再通过toString方法转成字符串.只是IO流只是操作数据,并不会真的拥有
数据.那么内存流既然没有把数据写出去,这些数据内存流是如何保存的呢?
StringBuilder 是把数据实实在在的写到自己的字符序列,也就是它真正
拥有过数据.那么内存流没有把数据写出去,真正拥有过这个数据吗?

实在不明白,查阅了api才发现:数据都是先写入一个byte数组(缓冲区会
随着数据的不断写入而自动增长),通过toByteArray()或者toString()
再将数据写出去.
那么现在就明白了,IO还是只操作数据...嘿嘿嘿...

* */
public class Demo05 {
public static void main(String[] args) throws IOException {
File fi = new File("c:\\stu.txt");
String s = readTxt(fi); //尽量保证输出操作由顾客控制.因此返回字符串.
System.out.println(s);
}
//接受一个文件,将文件数据封装成字符串,并返回.
public static String readTxt(File fi) throws IOException {
FileInputStream fs = new FileInputStream(fi);
byte[] by = new byte[5];
ByteArrayOutputStream bs = new ByteArrayOutputStream();
int i =0;
while((i = fs.read(by))!= -1)
{
bs.write(by,0,i); //不管读到哪里,因为bs都只是接受的,所以循环到-1停止,再一次转成字符串就好了.
}
return bs.toString();
}
}


六:

/**
* 6、 自定义字符输入流的包装类,通过这个包装类对底层字符输入流进行包装,
让程序通过这个包装类读取某个文本文件(例如,一个java源文件)时,
能够在读取的每行前面都加上有行号和冒号。

思路:
1.首先是字符的,输入的包装类,底层自然是一个一个读取字

符.那么该
包装类中必然要有read()方法.
2.对于read()的数据如何让其具备一行一行的数据记录呢?必须要有一个
变量,这个变量如何去定义,怎么样这个变量才会变化呢?
3.每次一行的读取数据,那么可以在包装类中定义一个StringBuilder用来
接受数据,当读到行末尾的时候,就返回这个StringBuilder的字符串.

总结:
1.java中读取的一行一行数据,也是依靠底层一个一个读取,然后根据\n来判断读取到行末尾,再一次性返回.
2.对于判断结束标记,我们在自定义返回数据的时候,就需要给定一个可以让其结
束的标记(null 或者 -1 等等).
3.对于StringBuilder,其中接受的数据,并没用换行操作(因为底层中,一次一个字节读取,对于\r\n只是一个判断依据,并没有存储起来),因此我们需要手动,对于行结尾要添加"\r\n"以便输出时候识别换行.

思考:
java中,一次读取一行是提高了效率的操作.但是,一次读取一行其底层也是一个一个读取的操作.那么,怎么样能够证明,貌似一样的操作手法一次一行就效率更高呢?

查阅api发现,如果没有定义缓冲区,每次读取后,都要将数据从字节转换成字符数据而后返回,这样会效率极低.
个人认为:
不管是字节流还是字符流,底层都是在读取字节.只是字符流加入了一个可以让我们识别的一个编码表.如果一次读一个字符,先存储起来,等到读到行末尾再一次输出.
那么效率应该是提高在"数据返回"这个关键点上.而在底层读取啊,还有转换呀,
这些方面是没有提高效率的.
* */
public class Demo06 {
public static void main(String[] args) throws IOException {
File fi = new File("c:\\stu.txt");
String s = LineFile(fi);

System.out.println(s);
}

public static String LineFile(File fi) throws IOException {
MyLineReader mr = new MyLineReader(new FileReader(fi));
String s = null;
StringBuilder sb = new StringBuilder(); //为了让程序拥有自主控制输出,因此创建.
mr.setLen(0); //设置开始行号.
while((s = mr.MyreadLine())!= null)
{
sb.append( mr.getLen() + ":" + s + "\r\n");
}
return sb.toString(); //将数据返回,让客户拥有输出权利.
}
}


class MyLineReader
{
private int len; //自定义行数变量
private Reader fi; //读取数据流.
public MyLineReader(Reader fi)
{
this.fi = fi;
}
public String MyreadLine() throws IOException
{
len ++; //读取一行之后,len自增.
StringBuilder sb = new StringBuilder(); //将数据存储起来.
int i = 0;
while((i = fi.read())!= -1)
{
if(i == '\r')
continue;
if(i == '\n') //当独到行标记时候,将数据全部返回.
return sb.toString();
sb.append((char)i); //没有到达行标记就继续读取.
}
if(sb.length() != 0) //StringBuilde

r的长度不为0,就继续返回数据.
return sb.toString();
return null; //否则返回空.因为调用方法时候,是依靠返回是不是null来判断,是不是应该结束.
}
public int getLen() { //get,set方法,用来获取行号和设置行号.
return len;
}
public void setLen(int len) {
this.len = len;
}
}



七:

/** 思路:
* 这是一个线程题目,那么线程中,首先要明确的主线程本身就是一个线程.
其他线程是通过new Threa()来获取一个新的线程.
总结:
1.线程之间如果不是人为操作的话,之间是不会产生逻辑的.
2.对于线程的行动路径,要清楚,它是执行在什么区域呢?主线自然在主函数块执行.而我自己创建Threa线程,在指定的run()方法中执行.
3.关键点在于,主线程并不会要等到Thread的线程执行完才会执行,因为它们之间并没有人为的控制操作.也就是在主线程执行到t.run()的时候,这个是属于主线程的执行路径,必须等到run执行完毕才继续下一行代码.而读取到t.start()时候,这就是开启另一个线程执行了,主线程并不需要执行操作,它会继续往下执行.

思考:
线程之间,如何判断是不是要求主线程亲自执行,就是依靠thread它所协商的线程有没有执行,也就是t.start()有没有执行,这才是协商的线程执行地.
另外,理论只是理论.并不是在现实生活中就真的如此精确.因为计算机就是一个复杂的功能体.我们能确定的是我们的需求控制,但是具体执行,要依靠计算机内部的更复杂协调.因此,对于线程控制,我们只能站在一个理论的基础上来进行控制.
* */
/*7、 分析以下程序运行结果,说明原理。(没有分析结果不得分)

public class ThreadTest {
public static void main(String args[]) {
MyThread t = new MyThread();
t.run();
t.start();
System.out.println("A");
}
}

class MyThread extends Thread {
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("B");
}
}
*/
public class Test7 {

public static void main(String[] args){

System.out.println("该程序运行结果:\rB\rA\rB"
+"\r程序分析:MyThread类继承Thread类,并复写run方法,使该线程休眠3秒后打印B。"
+"\r主函数创建MyThread类对象,调用t.run(),启动t.start(),打印A。"
+"\r原理分析:\r1.t.run()是调用MyThread里的方法run(),这时主线程休眠3秒后首先打印出一个B。"
+"\r2.t.start()是启动线程MyThread,并执行方法run(),这时线程t休眠3秒。"
+"\r3.在2执行的时候同时执行打印A, 因为2要休眠3秒,所以先打印出一个A。");
}

}


八:


/*8、 将字符串中进行反转。abcd

e --> edcba*/

//这是一个极为简单的字符串倒序排列.还是那句话,看到字符串,想他的亲兄弟数组.
//将字符串转成数组,对元素进行倒序遍历,然后存入StringBuilder,再打印.
public class Demo08 {
public static void main(String[] args) {
String s = "abcdefg";
StringBuilder sb = new StringBuilder();
char[] ch = s.toCharArray();
for(int x = ch.length -1 ; x >= 0; x --)
{
sb.append(ch[x]);
}
System.out.println(sb.toString());

}
}



//第二种.直接转换,
public class Test8 {

public static void main(String[] args){
//StringBuffer作为一个容器可以对字符串内容进行修改
StringBuffer sb = new StringBuffer("abcde");
System.out.println(sb.reverse());
}
}


九:


/*9、 写一方法,打印等长的二维数组,要求从1开始的自然数由方阵的最外圈向内螺旋方式地顺序排列。 如: n = 4 则打印:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
* */
public class Test9 {
public static void main(String[] args){
//从键盘输入大于0的整数数得到螺旋排列的二维数组
print(8);
}
//判断从键盘输入的值的合法性并获取该值
// public static int getNum(){
// boolean judge = true; //定义Boolean类型的值来判断是否跳出循环
// int result = 0;//定义键盘获取的初值
// while(judge){
// Scanner scan = new Scanner(System.in);
// System.out.println("输入一个大于0的整数:");
// try{
// result = Integer.parseInt(scan.nextLine());
// scan.close();
// if(result<0){
// throw new Exception();//不符合则抛出异常
// }
// judge = false;
// }catch(Exception e){
// System.out.println("输入值非法,请重新输入");
// }
// }
// return result;
// }
//得到键盘输入的数值并打印二维数组
public static void print(int num){
//定义等长的二维数组
int[][] arr = new int[num][num];
int n = arr.length;
int count = 0;
int max = 0;
recArr(arr,n,count,max);
printArr(arr); //执行打印
}
public static void recArr(int[][] arr,int n,int count,int max){
//递归控制条件
if(n>0){
//纵坐标控制值
int k = 0;
//(n-1)*4代表每一圈的数值范围
for(int i=0;i<(n-1)*4;i++){
//在上边赋值
if(iarr[count+0][count+i] = ++max;
}
//向右边赋值
else if(i<2*n-2){
arr[count+k++][arr.length-1-count]=++max;
}
//在下边赋值
else if(i<3*n-3){
arr[arr.length-1-count][(k--)+count]=++max;
}
//向左边赋值
else if(i<4*n-4)

{
arr[arr.length-1-(k++)-count][0+count]=++max;
}
}
//当n为奇数时,存在n=1的情况,最里圈只有一个数
if(n==1){
arr[arr.length/2][arr.length/2]=max+1;
}
//增加圈数
count++;
//边界每次减少两个数值
n -= 2;
//递归
recArr(arr,n,count,max);
}
}
//打印二维数组
public static void printArr(int[][] arr){
//二维数组需要双重循环打印
for(int[] ar : arr){
for(int a : ar){
if(a<10)
System.out.print(" "+a+" ");
else
System.out.print(a+" ");
}
System.out.println();
}
}
}

十:

/**
* 10、 28人买可乐喝,3个可乐瓶盖可以换一瓶可乐,那么要买多少瓶可乐,
* 够28人喝?假如是50人,又需要买多少瓶可乐?
(需写出分析思路)
思路:
1.该需求中拥有几个变量?一个是盖子的数目,一个是需要购买的可乐数目.
一个是可以喝的可乐数.
2.定义一个函数,接受一个int值,这个值就是我们需要满足多少人喝可乐.
3.那么对于多少人和可乐,和买多少可乐之间如何产生联系呢?也就是,我们
喝的可乐数,肯定< 可供喝可乐的数目.(如果等于就是够喝了,够喝了还需要计算吗?)
总结:
1.逻辑题目,就是考验人的一个逻辑分析能力,对于代码的需求并不高.
2.既然是逻辑,那么你给了我什么,你需要我给你什么?就本题而言,客户
给我一个满足多个人喝可乐的人数,而需求我返回我买多少可乐够他们喝.
3.那么我们定义函数的时候,就应该把客户的需求做为一个参数传递进来,因为这个是他指定的,那么我就在返回值的时候给他返回一个他需要的购买数量.
4.然后进入功能函数之后,我只有一个对方给定的一个人数,那么我要想,我如何能够让这个给定的人数与我的程序产生关联呢?
5.我够买的数量是我要返回的值,那么怎么样我就需要购买呢?瓶盖够三个自然不用购买,那么谁会变化?自然是可以喝的可乐增加,瓶盖变成1;不够三个的话,我就要去买了,买了之后,可以喝的可乐就多了1,瓶盖也多1.
6.当可以喝的可乐都和人数相等了,就不用再买了...但是这里为什么没有<=person呢?如果等于了,条件还满足,还要执行.都够喝了,执行还有意义吗?

思考:
1.逻辑,逻辑,不行我就画画好吗?
2.不行我就把朋友兄弟都模拟参与进来可以吗?
3.逻辑练习需要一个过程,也急不得,多多的适应吧...
* */
public class Demo10 {
public static void main(Strin

g[] args) {
int person = 13; //自定义需求.
int cola = run(person); //接受需要购买的次数.
System.out.println(cola);
}

public static int run(int person) {
int gaizi= 0; //盖子数
int cola = 0; //可以喝的可乐数
int gouMai = 0; //需要购买的可乐数.
for(;cola < person; cola ++) //无论如何,可以喝的可乐都在增加.
{
if(gaizi == 3) //当盖子够三个了,盖子数又定义为一个.
{ //可以喝的可乐在上面已经增加了.
gaizi = 1;
}
else
{
gouMai ++;//如果不够三个,那么就要去买了,买一瓶,盖子自然增加一个.
gaizi ++;
}
}
return gouMai; //把购买的次数返回.
}
}

十一:



/**
* 11.題目:编写一个类,在main方法中定义一个Map对象(采用泛型),
* 加入若干个对象,然后遍历并打印出各元素的key和value。
*
* 思路:
* 其实也没有什么需求思路,这个就是很简单的map的遍历方法应用.
*
* 总结:
* map遍历需求必须还是那句话,想到set亲姐妹.
* 一个是通过EntrySet()将关系对存入set集合.
* 一个是通过KeySet()将key值存入set集合.
* 思考:
* map集合的两种遍历方式要牢牢记住.一个存入的是key和value的关系.一个
* 存入的是实实在在的key;
* */
public class Demo11 {
public static void main(String[] args) {
Map ma = new HashMap();
ma.put("wo", 22);
ma.put("he", 27);
ma.put("ni", 29);
ma.put("xiangyue", 33);
ListMap(ma); //尽量封装起来.
}

public static void ListMap(Map ma) {
Set> se = new HashSet>();
se = ma.entrySet();
Iterator> it= se.iterator();
while(it.hasNext())
{
Entry en = it.next();
System.out.println(en.getKey() + ":::" + en.getValue());
}
}
}


十二:


/*
题目:方法中的内部类能不能访问方法中的局部变量,为什么?
答:方法中的内部类访问局部变量的时候,局部变量需要被 final 关键字修饰。
因为方法中的代码是由上而下顺序执行的,方法运行结束后,局部变量就被销毁,
内部类的生命周期可能会比局部变量的生命周期长;看下面的代码,方法中的内部
类 Inner.class 调用方法中的局部变量 x ,正常调用的时候内部类能够调用到方
法中的局部变量,并将内部类对象 inner 返回,正常调用结束后,如果方法中的局
部变量 x 没有被 final 关键字修饰,x 则会被销毁,我们再通过反射的方式去调用 x
的时候则会发现找不到变量 x ,如果局部变量 x 被 final 关键字修饰后,则 x 在方
法运行结束后不会被销

毁,而是常驻在内存中直到 JVM 退出,这样再通过反射调用的
时候依然可以找到 x 。
*/
public class Test12 {
public static void main(String[] args) throws Exception {
Outer outer = new Outer(); // 正常调用
Object object = outer.outerfun();

Class clazz = object.getClass(); // 反射调用
Method method = clazz.getMethod("innerfun");
method.invoke(object);
}
}

class Outer {
public Object outerfun() {
final int x = 5;
class Inner { //内部类Inner
public void innerfun() {
System.out.println(x);
}
}
Inner inner = new Inner();
inner.innerfun();
return inner;
}

}



十三:
/**
* 13:定义一个交通灯枚举,包含红灯、绿灯、黄灯,
* 需要有获得下一个灯的方法,例如:红灯获取下一个灯是绿灯,绿灯获取下一个灯是黄灯。
*
* 思路:
* 1.既然是定义一个枚举,那么就是enum类.
* 2.该枚举类中有三个元素.红,绿,黄.
* 3.拥有获取下一个灯的方法,这个方法可以返回枚举元素,什么类型?自然就是我们定义的枚举类型.
* 总结:
* 1.枚举说实在的,就是把自己的子类都放自己身上.自己的这些元素,就是
* 自己的子类对象.
* 2.子类复写父类方法.返回类型如果是枚举元素,那么就是枚举类型.
* 3.enum,才代表是枚举类.枚举中的元素要写在最开始位置.
* 4.枚举也可以说,在java中属于一个较安全的体系.对元素进行了必要的限制操作.而且是反射无法破解的.
* 思考:
* 1.枚举是一个特殊的类.之所以是特殊的,我认为它所操作的对象不是类中的方法,而是自己的一个一个子类对象元素.
* 2.枚举这个类,应该和java普通类分别看待,属于比较另类的一种.
* 3.枚举既简单也复杂,因为调用了复写,内部类,等等一些用法.还需要
* 多多揣摩.
*
* */
public class Demo13 {
public static void main(String[] args) {
traffic s = traffic.RED.next(); //调用枚举元素以及方法.返回枚举元素.
System.out.println(s);
}
}
enum traffic
{
RED
{
public traffic next() //复写父类的抽象方法.返回还是枚举元素.
{
return GRE;
}
},GRE
{
public traffic next()
{
return YEL;
}
},YEL
{
public traffic next()
{
return RED;
}
};
public abstract traffic next(); //枚举的抽象方法,子类需要复写.
}


十四:

/**
* 题目:编写一个类,增加一个实例方法用于打印一条字符串。
* 并使用反射手段创建该类的对象, 并调用该对象中的方法
*
* 思路:
* 1.首先有那么一个类,里面有那么一个方法,就是打印一条字符串.
* 2.在主函数下利用反射创建该类的字节码对象,再获取构造函数,newInstance()该类的对象.然后通过反射将该对象方法调用.
*

总结:
* 1.看到反射,先找字节码,没字节码就没有反射.
* 2.了解创建字节码对象的三种方式,1>类名.class.2>象.getClass();3>Class.forName(类全名字符串);
* 3.对于权限一定要注意,分三种,一种是默认,第二种,是public,第三种是private
public不需要加入Declared.默认需要加入Declared,private不但要加,而且还要setAccessible(true)暴力反射
*
* 思考:
* 反射实在是逆天了.连private都不放过了.讲一个小小的故事对反射进行一个思考:
* 有一个农村,村里有100户人家,有一个地主家里有自己的生产工具.这个工具呢,有些是可以给大家用的(public),有些可以协商使用的(默认修饰),有些是不想给别人用的(private).可是,有一天,农改来了,地主老財开始遭殃了.本来公有的,拿去给平民用了,有些默认的也给平民使用了,甚至,连自己私有的,也通过暴力手段让平民使用了...一个好的效果就是,平民都可以更好的利用了这个工具,使生产提高了效率.毕竟一个工具在地主老财家里使用是有限的,拿出来,工具的使用价值,使用频率就变高了...可是不好的是,地主老财很不开心.因为我既然私有的,你应该给我一些人格保证的不是?连我私有的物品,你都公开了,那我人格是不是要受点伤害了???
* 因此,对于反射,我们需求考虑该反射对象的修饰类型.如果是私有的,我们需要慎重考虑一下.因为.private自然有private的道理.
*
* */
public class Demo14 {
public static void main(String[] args) throws Exception {
// Class clazz = PrinString.class; 第一种创建class字节码方式
// PrinString s = new PrinString(); 第二种方式
// Class clazz = s.getClass();
Class clazz = Class.forName("cn.itheima.demo01.PrinString"); //第三种方式.
//调用空参构造,直接Constructor不可以滴,因为是默认修饰.public的才可以.要是private,一定还要暴力反射.
Constructor con = clazz.getDeclaredConstructor();
PrinString ps = (PrinString)con.newInstance();
Method me = clazz.getMethod("run", null); //获取方法
me.invoke(ps, null); //执行方法.
}
}
class PrinString
{
public void run()
{
System.out.println("你好吗?反正我很好!");
}
}



十五:
/**
* 把当前文本中的所有文本拷贝,存入一个txt文件,统计每个字符出现的次数
* 并输出,例如 a : 21次 b: 12次.....
*
* 思路:
* 1,首先明确源文件,和目标文件.这是一个文件拷贝.流选择字符流
* 2.如何去统计每个字符出现的次数呢?就需要遍历,怎么遍历?每次我们的文本都是一行一行的读取(选用高效率的话),整理字符串,并进行遍历,把遍历的字符和对应的出现次数存入一个map集合.
* 3.存入map集合的数据,我们需要通过迭代,entrySet或者keySet来进行.这样我们就获取到每个字符出现

的次数...
*
*

* 总结:
* 1.迭代的字符串,应该是一个整体的,不应该分段处理,这样程序就是一个连续.如果分段处理,我们每次调用程序,程序会根据每次调用做出分别处理,这样不会得到我们想要的结果.
* 2.map记录的value值,也就是次数,需要每次在之前的value基础上递增.而不是依靠外部的一个变量去实现,因为字符出现是没有规则的.比如第一次出现了a,那么计数器增加一次,而第二次出现的是b,计数器还增加1,而此时b才第一次出现.
思考:
* 根据面向对象的思想,我们知道,每一个功能体,我们要尽量把它独立起来,这样方便我们复用.
* 都是一些很小的细节问题.但是,却很关键.整体需要一个清晰的思路是必要的.
*
* 源文件---通过IO -----目标文件. 第一个任务完成.
* |------获取字符串---遍历进Map---迭代Map集合---获取对应字符和次数 . 第二任务完成.
* */
public class Demo08 {
public static void main(String[] args) throws IOException {
File fiReade = new File("c:\\a.txt");
File fiWrite = new File("c:\\b.txt");
IORW(fiReade,fiWrite);
}

//1.这是一个将数据分离成字符串,并输出到指定文件的程序.(1)分离字符串,进行后续操作(2)写入文件,完成一个写入文件需求
public static void IORW(File fiReade, File fiWrite) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(fiReade));
BufferedWriter bw = new BufferedWriter(new FileWriter(fiWrite));
StringBuilder sb = new StringBuilder();

String s = null;
while((s = br.readLine())!= null)
{
sb.append(s); //先装进字符串.等结束后一次性给调用者.
bw.write(s);
}
ergodic(sb.toString()); //这是一个注意.应该所有数据完成之后才可以进行迭代遍历出次数.
br.close();
bw.close();

}

//2.接受分离出来的字符串,然后对字符串进行遍历,并将结果存入map集合.
public static void ergodic(String s) {
Map ma = new HashMap();
char[] ch = s.toCharArray();

for(int x = 0; x < ch.length; x ++)
{
int num = 1;
if(!ma.containsKey(ch[x]))
{
ma.put(ch[x], 1);
} else
{
num =ma.get(ch[x]); //这是一个应该注意的,此时要记住之前的value值,再此基础上+1
ma.put(ch[x], ++num);
}
}
ergodicMap(ma);
}

//3.将map集合进行迭代,
public static void ergodicMap(Map ma) {
Set se = new TreeSet();
se = ma.entrySet();
Iterator it = se.iterator();
while(it.hasNext())
{
Entry en = (Entry)it.next();
System.out.println(en.getKey() + " : " + en.getValue() + " 次");
}
}
}

十六:

/**
* 题目:编写程序,循环接收用户从键盘输入多个字符串,
* 直到输入“end”时循环结束,并将所有已输入的字符串

按字典顺序倒序打印。
*
* 思路:
* 1.循环接受.简单文本扫描器,Scanner();
* 2.接受的数据是一个字符串,将字符串转成字符数组.
* 3.通过Arrys里面的sort()方法,将字符进行默认升序排列.
* 4.对于这个已经排序了的数据,再从后往前遍历.
* 总结:
* 1.键盘接受两个方法,一个是流操作,一个就是默认简单文本扫描.
* 2.对于接受到的数据,无非两种情况,一种就是end该结束,一种就是不是end要怎么操作?
* 3.还是那句话,字符串要找亲兄弟---数组.
* 思考:
* 关于字符串的事,一般情况下还是数组.对于数组操作,要想到Arrays这个数组工具类.排序,无非就是对数组遍历.
*
* */
public class Demo16 {
public static void main(String[] args) {
SystemIn();
}

public static void ListChar(char[] ch) { //倒序打印.
for(int x = ch.length - 1; x >= 0; x --)
{
System.out.print(ch[x]);
}

}

public static void SystemIn() {
Scanner s = new Scanner(System.in);

while(true)
{
String str = s.nextLine(); //字符串每次需要重新获得,因此要定义在循环内.
if(str.equals("end"))
{
System.out.println("输入结束");
break;
}
else
{
char[] ch = str.toCharArray(); //字符串转数组
Arrays.sort(ch); //数组方法,默认字典升序排列.
ListChar(ch); //再通过倒序打印.
}
}
}
}


十七:


/**
* 一个ArrayList对象aList中存有若干个字符串元素,
* 现欲遍历该ArrayList对象,删除其中所有值为"abc"的字符串元素,
* 请用代码实现。
*
* 思路:
* 1.定义一个ArrayList,并存储各种Stirng类型元素.
* 2.list集合,也是有角标的,那么如何利用?当这个元素和"abc"
* 相同时,就删除.
* 总结:
* 程序很简单,主要就是利用集合的角标这一特点.
* 思考:
* 集合元素的删除,可以依靠其角标,但是,并不是所有的遍历方式都可以
* 这样删除元素的,普通Iterator以及高级For是不可以的.会出现java.util.
* ConcurrentModificationException 异常错误.同步修改错误.也就是
* 普通iterator和增强for底层都是依靠hasNext方法和next方法,对元素只是一个识别过程,并不会去操作元素.但是for是在操作每一个元素,因此也可以
* 对自己操作的元素进行操作.
* 因此,我们可以想象,在java中,存在许多的抽象概念.比如引用和具体对象,
* 一个是指向,一个是实体.指向只是一个索引,而实体就是实实在在的个体.遍历也是一样,Iterator遍历元素,只是对这个元素进行了识别,有还是没有?存在还是不存在?但是并没有把具体元素拿在"手上"具体操作.因此,对于"手上"都没有具体元素的操作,怎么能够拿来对元素进行改变呢?
* */
public class Demo17 {
public static void main(

String[] args) {
ArrayList al = new ArrayList();
String s = "abc"; //需要删除的指定字符串
al.add("abcd");
al.add("abc");
al.add("abcded");
al.add("abcdabc");
ArrayList newList = remove(al,s);
System.out.println(newList);
}

public static ArrayList remove(ArrayList al, String s) {
for(int x = 0; x < al.size(); x ++)
{
if(al.get(x).equals(s))
{
al.remove(al.get(x));
}
}
return al; //返回需要的集合
}
}

十八:

/**
* (1) 写一个Properties格式的配置文件,配置类的完整名称。
(2) 写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射 的方式运行run方法。
思路:
1.对于存在的配置文件,首先要通过FileInputStream与其关联.
2.创建properties对象,这个集合再通过load()方法,将流数据
读取到集合中.
3.通过集合的getProperty()将配置文件中对应的数据读取出来,字符串接受
4.该字符串(key的value值)就是我们需要获取到的类全名.用Class的forName()就可以获取到字节码.
5.然后就是反射操作.
总结:
1.读取配置文件,其底层需要通过IO流.
2.Properties集合通过load将集合关联的数据写入properties中.
3.通过getProperty(键)将对应的值读取.
思考:
读取配置文件,还有一个专门读取配置文件的集合.这是为什么?说明配置文件
的读取在java实际开发中是属于重量级的.集合中有很方便的获取key和值的方法.可以对集合进行遍历(StringPropertyNames()返回一个set集合),这一点和map中其他集合相似.
* */
public class Demo18 {
public static void main(String[] args) throws Exception {
FileInputStream is = new FileInputStream("c:\\stu.properties");
Properties pp = new Properties();

pp.load(is);
String str = pp.getProperty("className");
Class clazz = Class.forName(str);
Object obj = clazz.newInstance();
Method me = clazz.getMethod("run", null);
me.invoke(obj,null);
}
}



十九:


/*
* 6、编写一个类,增加一个实例方法用于打印一条字符串。
* 并使用反射手段创建该类的对象, 并调用该对象中的方法。
* */
//对象的打印方法
class Println{
public void println(String str){
System.out.println(str);
}
}
public class Test19 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("com.itheima.Println");//通过反射的方式获取该类的字节码文件
Method method = clazz.getMethod("println",String.class);//获取字节码对象对应的方法,并指定参数类型。
method.invoke(clazz.newInstance(),"黑马程序员训练营我来了");//运行对象中的方法,并传入参数。
}
}


二十:


/**
* 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。
*
* 思

路:
* 1.获取随机数,就用Random;
* 2.随机数不能重复,就用set集合.
* 3.长度为10,让set集合长度<=10;
* 总结:
* 不能重复,要装元素,就是set集合.
* 思考:
* 对于元素的操作,可重复和不可重复这这种特性用的比较多,要分清楚具体要用到什么集合.因为需求是要求随机数,所以尽量还是不要排序好.因此使用HashSet;
*
* */
public class Demo19 {
public static void main(String[] args){
Set se = MyRandom();
System.out.println(se);
}

public static Set MyRandom() {
Random rd = new Random();

Set se = new HashSet();
while(se.size() < 10)
{
int i = rd.nextInt(20) + 1;
se.add(i);
}
return se;
}
}


二十一:


/**
* 声明一个共享数组,起两个线程,
两个线程分别隔一段时间(可以写一个随机数),给数组中添加数据,
每一个线程为数组添加3个数据即可。

思路:
1.数组是共享的,线程要有两个.拥有数组的对象只能有一个,Thread需要创建两次.
2.随机一个数字,存入数组.
3.对于判断条件,就是数组长度<= 我们指定的值.
总结:
1.共享的数据只能一份,那么在创建这个共享数据对象时候,就只能有一个对象.
2.实现了Runnable的类,只是一个协商提供线程运行的执行地.
3.共享数据是实实在在的,而线程是虚拟的.
4.wait和sleep的用法.wait是Object中的方法,使当前线程放弃cup执行权.同时可以等待时间...而sleep是Thread中的方法,使线程等待一段时间,但是线程并没有释放cup使用权.
思考:
1.线程之所以要实现Runnable而不继承Thread,是因为继承只支持单继承,限制了类的扩展性.
2.线程是一个虚拟的事物.这个虚拟的事物通过协议的执行地(run()方法)在这个区域执行线程所需要执行的数据.如何保证执行同一个资源呢?就是对于执行地只需求存放一份.如果通过创建两个ArrayAdd,那么这就是两个执行地.应该注意一下.
3.对于wait和sleep的用法.应该特别注意一下.如果我们想要让该线程放弃cup执行权(放弃锁),那么就可以使用wait来控制,如果只是让线程等待一段时间,那么就使用sleep()来操作.

* */
public class Demo21 {
public static void main(String[] args) {
int[] in = new int[6]; //需要控制因素
ArrayAdd aa = new ArrayAdd(in); //既然要共享资源,那么这里就只能创建一个线程执行地.
for(int x = 0; x < 2; x ++)
{
new Thread(aa).start(); //创建多个线程.
}
}
}
class ArrayAdd implements Runnable
{
private int[] in; //共享的资源,在创建时候,只能拥有一份.
private int num = 0;
ArrayAdd(int[] in)
{
this.in = in;
}
public void run()
{
while(num < in.length)
{

synchronized(in)
{
if(num < in.length)
{
try {
T

hread.sleep(500); //线程只是等待一会时间,并没有释放锁.
} catch (InterruptedException e1) {
e1.printStackTrace();
}
Random rd = new Random();
int i = rd.nextInt(20) + 1;
System.out.println(Thread.currentThread().getName() + "添加了:" + i);
in[num] = i;
num ++;
try {
in.wait(1000); //放弃cup,放弃锁.让其他线程拿锁.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else
{
break;
}
}
}
}
}



二十二:


/**
* 复制一个文件夹到另一个目录下.

复制文件夹,首先需要想到递归.因为文件夹是一个层次目录.
其次,如何递归呢?递归什么?当然,只有是文件夹的时候才递归,那么判断是文件夹还是文件就是必要的.是文件夹就递归,是文件就用流读写.
再次,文件夹的命名和文件的命名.两者都需要在旧文件名称和新文件路径命名.我们需要知道,当旧文件夹通过listFiles获得下一级文件时候,这时候,我们就要想到,
新文件夹和旧文件夹的路径已经相差一级了.

思考:
该题的难点就在于新旧文件和文件夹的路径层次同步问题.每当listFiles之后,新旧文件或文件夹就有了一个层次的差距,具体如何补足这一个层次差,只要知道 --
旧文件名称和新文件路径命名 -- 就可以了.

* */
public class Demo02 {
public static void main(String[] args) throws IOException{
File oldFile = new File("c:\\aaa");
File newFile = new File("e:\\");
MyCopy(oldFile,newFile);
}
public static void MyCopy(File oldFile,File newFile) throws IOException
{
if(!oldFile.exists()) //判断源文件是不是存在,如果不存在,声明.
{
System.out.println("文件不存在");
}
//新的目录,加上旧的文件名.就成了一个新的文件.但是此时还没有具体文件.
File f = new File(newFile.getPath() + "\\" + oldFile.getName());
System.out.println("f:"+f);
f.mkdir(); //创建新的文件夹 &&&重要的一部分.这里是建立一个具体的文件夹,此时新旧文件夹都相同了.
File[] files = oldFile.listFiles(); //列出旧文件夹里的所有文件.
for(File file : files) //遍历这个文件夹里面的文件..
{
if(file.isDirectory()) //如果还是文件夹,就递归.
{
MyCopy(file,f);
}else //否则就可以,用IO流来读写文件了.
{
BufferedInputStream bis = new BufferedInputStream(new
FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(f.getPath() + "\\" + file.getName()));
int b = 0;
while((b= bis.read())!= -1)
{
bos.write(b);
}
bis.close();
bos.close();
System.out.println(file.getName() + " 拷贝完成!");
}
}
}
}



二十三:

/**
* 写一个ArrayList类的代理,实现

和ArrayList类中完全相同的功能,
* 并可以计算每个方法运行的时间。
* 思路:
* 1.目标类是谁?ArrayList().给这个目标类定义一个代理.那么首先要确定,它的父类接口是谁?List(); 因为创建代理,需要和需要代理的类实现共同的接口.
* 2.确定了父类接口之后,如何去创建目标的代理呢?通过Proxy.newProxyInstance()来创建,参数有三个:1,父类(list)的类加载器;2,通过该对象字节码获取的接口.3.调用程序实现的接口子类.(通过内部类形式)
* 3.对于调用程序实现的接口子类需要重写接口中的invoke方法(对象,方法,方法参数).然后对该方法进行一些设置,method.invoke(代理的对象,该对象的参数类型);这就是在代理内部对方法进行了设置.加入我们需求的一些信息.比如时间统计.
*
* 总结:
* 1.代理类的出现,使程序可以自由地实现一些额外的系统功能.
* 2.创建代理的方式.1,与目标类拥有共同的实现接口.2,目标类的子类也可以用作代理.(需要CGLIB库)
* (更多不太明白,需要进一步学习)
*
* */
public class Demo23 {
public static void main(String[] args) {
final ArrayList al = new ArrayList(); //必须final修饰.
List proxy = (List)Proxy.newProxyInstance
(List.class.getClassLoader(), //父类类加载器.
ArrayList.class.getInterfaces(), //类的接口
new InvocationHandler() { //内部类.是一个实现了调用程序实现的接口子类

@Override
//实现了这个接口复写invoke方法. (对象,方法,方法参数)
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long beginTime = System.currentTimeMillis();
Thread.sleep(10);
Object reVal = method.invoke(al,args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "运行了:" +
(endTime - beginTime) );
return reVal;
}
});
proxy.add("黑马程序员");
proxy.add("你好");
proxy.add("程序员");
System.out.println(proxy.toString());

}
}



二十四:
/**
* 存在一个javabean,设置string,Boolean,double,integer
* 的默认初值为https://www.sodocs.net/doc/d716944091.html,,true,0.01D,100
*
* 思路:
* 1.创建一个类,该类是标准javbean,并拥有一些自定义属性.
* 2.获取到该类的字节码文件,并通过字节码创建该类对象.
* 3.获取BeanInfo对象.通过Introspector.getBeanInfo(类字节码)
* 这样就将该类的信息封装到了BeanInfo对象中.
* 4.通过beanInfo.getPropertyDescriptors()返回一个数组,将信息
* 存入数组中.
* 5.遍历该PropertyDescriptors类型数组,并将数组元素通过各种
* 方法获取到信息.
* 6.用属性类型与提供的类型想匹配,然后赋值.
* 总结:
* 1.javaBean是一个协议类,也就是该

类拥有一些功能,主要用于对目标类进行字段的操作,但是,必须要按照javaBean自己提供的一个标准,它才可以识别;
* 2.javaBean和反射有什么区别呢?javaBean是把数据先封装到自己中,然后再通过自己的方法,将这些信息提供出来.针对字段,并具有很好的普遍性.而反射可以获取到任何信息,也更具有针对性.
*
* 思考:
* JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。
* */
public class Demo24 {
public static void main(String[] args) throws Exception {
Class clazz = Class.forName("cn.itheima.demo01.testBean");
Object bean = clazz.newInstance();
BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
PropertyDescriptor[] ppd = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pro: ppd)
{
Object name = pro.getName(); //属性名称str,in等等
Object type = pro.getPropertyType(); //属性类型String,int等等
Method set = pro.getWriteMethod(); //set方法.
Method get = pro.getReadMethod(); //get方法.
if(!name.equals(".class"))
{
if(set != null)
{
if(type == Boolean.class || type == Boolean.class)
{
set.invoke(bean, true);
}
if(type == String.class )
{
set.invoke(bean, "https://www.sodocs.net/doc/d716944091.html,");
}
if(type == Integer.class || type == int.class)
{
set.invoke(bean, 100);
}
if(type == Double.class || type == double.class)
{
set.invoke(bean, 0.01D);
}
}
if(get != null)
{
System.out.println(type + " "
+ name + "=" + get.invoke(bean,null) );
}
}

}
}
}

class testBean{
private boolean b;
private String str;
private Integer i;
private double d;
public boolean isB() {
return b;
}
public void setB(boolean b) {
this.b = b;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public Integer getI() {
return i;
}
public void setI(Integer i) {
this.i = i;
}
public double getD() {
return d;
}
public void setD(double d) {
this.d = d;
}
}



二十五:

/**
* 从键盘接受一个数字,将这个十进制的数字转换成二进制并输出
* 到控制台,当输入"end"时,结束.
*
* 思路:
* 1.通过简单文本扫描器Scanner()来获取键盘输入的数据.
* 2.将数据转成字符串,兵分两路:一个是"end"结束,一个非"end"继续
* 3.当输入的不是"end"时,兵分两路,一个是数字一个非数字.
* 4.非数字,提示错误,数字就进行下一步操作.
* 5.创建StringBuilder,通过对数字的 /2%2的过程,将十进制
* 转成二进制.
* 6.将StringBuilder转成字符串打印到控制台.
*
* 总结:
* 1.对于判断多想想几种可能,这样对于出现的某些可能,我们就会
*有清晰的处理方法.
* 2.10转2进制,就是对于该数字/2%2

相关主题