2021-07-30

第一天:JavaSE基础加强

一、基础回顾

  1. 集合

1.1 集合的类型与各自的特性

集合: 存储对象数据 的集合容器。

单例集合

----------| Collection  单例集合 的根接口
----------------| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。
-------------------| ArrayList ArrayList底层是使用了Object数组实现 的。 特点: 查询速度快,增删慢。
-------------------| LinkedList LinkedList底层是使用了链表数据结构实现的。特点: 查询速度慢,增删快
-------------------| Vector 底层是使用了Object数组实现 的, 实现原理与ArrayList 是一致的,但是是线程安全的,操作效率低。
----------------| Set  如果是实现了Set接口的集合类,具备的特点: 无序,不可重复。
-------------------| HashSet 底层是使用了哈希表实现 的。 特点: 存取速度快。

HashSet存储元素的原理:
 	往hashSet添加元素的时候,首先会调用元素的hashCode方法得到元素的哈希码值,然后把哈希码值经过运算算出该元素存在哈希表中的位置。
	
	情况1:如果算出的位置目前还没有存在任何的元素,那么该元素可以直接添加到哈希表中。
		
	情况2: 如果算出的位置目前已经存在其他的元素,那么还会调用元素 的equals方法再与这个位置上 的元素比较一次。
	如果equals方法返回的是true,那么该元素被视为重复元素,不允许添加。如果equals方法返回 的是false,那么该元素也可以被添加。
------------------| TreeSet  底层是使用了红黑树(二叉树)数据结构实现的。 特点: 对集合中的元素进行排序存储、。

TreeSet要注意的事项:
	1.TreeSet添加元素 的时候,如果元素具备自然顺序的特点,那么TreeSet会根据元素 的自然顺序特性进行排序 存储。
	2.TreeSet添加元素 的时候,如果元素不具备自然顺序的特点,那么元素所属的类就必须要实现Comparable接口,把比较的规则定义在CompareTo方法上。
	3.TreeSet添加元素 的时候,如果元素不具备自然顺序的特点,那么元素所属的类就也没有实现Comparable接口,那么在创建TreeSet对象的时候必须要传入比较器对象。
	
	比较器 的定义格式:
			class 类名 implements  Comparator{
			}

双列集合
	----------| Map 存储的数据都是以键值对的形式存在的,键可以不重复,值可重复。
	-------------| HashMap 底层也是使用了哈希表实现的。
	-------------| TreeMap 底层也是使用了红黑树数据结构实现的。

1.2遍历集合


public static void main(String[] args) {
	// 遍历数组:
	String[] arr = new String[] { "xx", "yy", "zz" };

	// 1,增强的for循环
	for (String elt : arr) {
		System.out.println(elt);
	}

	// 2,下标的方式
	for (int i = 0; i < arr.length; i++) {
		System.out.println(arr[i]);
	}
}
----------------------------------------
public static void main(String[] args) {
	// 遍历List:
	List<String> list = new ArrayList<String>();
	list.add("aa");
	list.add("bb");
	list.add("cc");

	// 1,增强的for循环
	for (String elt : list) {
		System.out.println(elt);
	}

	// 2,下标
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i));
	}

	// 3,迭代器
	for (Iterator<String> iter = list.iterator(); iter.hasNext();) {
		String elt = iter.next();
		System.out.println(elt);
	}
}
--------------------------------------------
public static void main(String[] args) {
	// 遍历Set:
	Set<String> set = new HashSet<String>();
	set.add("dd");
	set.add("ee");
	set.add("ff");

	// 1,增强的for循环
	for (String elt : set) {
		System.out.println(elt);
	}
	
	// 2,迭代器
	for(Iterator<String> iter = set.iterator(); iter.hasNext() ; ){
		String elt = iter.next();
		System.out.println(elt);
	}
}
--------------------------------------
public static void main(String[] args) {
	// 遍历Map:
	Map<String, String> map = new HashMap<String, String>();
	map.put("aa", "xx");
	map.put("bb", "yy");
	map.put("cc", "zz");

	// 1,增强的for循环(Entry集合)
	for (Entry<String, String> entry : map.entrySet()) {
		System.out.println(entry);
	}
	
	// 2,增强的for循环(Key集合)
	for(String key : map.keySet()){
		System.out.println(key + " = " + map.get(key));
	}
	
	// 3,遍历值的集合
	for(String value : map.values()){
		System.out.println(value);
	}
}

2.泛型(Generic)

2.1当集合中存储的对象类型不同时,那么会导致程序在运行的时候的转型异常/

 泛型 的好处:
 	1. 把运行时出现 的问题提前至了编译时。
 	2. 避免了无谓的强制类型转换。
 	

 泛型在集合中的应用:
 	ArrayList<String> list = new ArrayList<String>();      true
 	ArrayList<Object> list = new ArrayList<String>();      false
 	ArrayList<String> list = new ArrayList<Object>();      false

 考虑到新老系统兼用性:
 
 	ArrayList   list = new ArrayList<String>();   true
 	ArrayList<String> list = new ArrayList();     true
 
注意: 在泛型中没有多态的概念,两边的数据必须要一致。或者是只写一边的泛型类型。

推荐使用: 两边的数据类型都写上一致的。
package cn.itcast.genecity;

import java.util.ArrayList;

public class Demo1 {
	
	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<String>();
		list.add("aaa");
		list.add("bbb");
		list.add("ccc");
//		list.add(123);  //添加了非字符串类型的数据。
		//把集合中的所有元素转换成大写。
		for(int i = 0 ; i < list.size() ; i++){
			String str =  list.get(i);
			System.out.println(str.toUpperCase());
		}		
	}
}

package cn.itcast.genecity;
/*
 自定义泛型:自定义泛型可以理解为是一个数据类型的占位符,或者是理解为是一个数据类型的变量。
 泛型方法:
	泛型方法的定义格式: 
		修饰符   <声明自定义泛型>返回值类型  函数名(形参列表..){
		
		}
注意:
	1. 在方法上的自定义泛型的具体数据类型是调用该方法的时候传入实参的时候确定的。
	2. 自定义泛型使用的标识符只要符合标识符的命名规则即可。
 需求: 定义一个函数可以接收任意类型的参数,要求函数的返回值类型与实参的数据类型要一致。
 */
public class Demo2 {
	public static void main(String[] args) {
		Integer i= print(12); // Integer
		String str = print("abc");
	}
	//<T> 把T字母声明为自定义 的泛型、
	public static <abc> abc print(abc o){
		return o;
	}
}

package cn.itcast.genecity;

import java.util.ArrayList;
import java.util.Comparator;

/*
 泛型类
 泛型类的定义格式:	
 	class 类名<声明自定义的泛型>{
 	
 	} 
注意的事项: 
 	1. 在类上自定义的泛型的具体数据类型是在创建对象的时候指定的。
	2. 在类上自定义了泛型,如果创建该类的对象时没有指定泛型的具体类型,那么默认是Object类型。	
 */
class Worker implements Comparator<Worker>{
	@Override
	public int compare(Worker o1, Worker o2) {
		// TODO Auto-generated method stub
		return 0;
	}	
}
//自定义 一个集合对象
class MyList<T>{
	Object[]  arr = new Object[10];
	int index = 0;
	public MyList(){
	}
	public void add(T o){
		arr[index++] = o;
	} 	
}
public class Demo3 {
	public static void main(String[] args) {
		MyList<String> list=  new MyList<String>();
		list.add("123");
		MyList list2 = new MyList();
		new ArrayList<String>();
	}
}

package cn.itcast.genecity;
/*
 泛型接口:
 泛型接口的定义格式:
 	interface 接口名<声明自定义的泛型>{
 	
 	}
 
在接口上自定义泛型要注意的事项:
 	1. 在接口上自定义泛型的具体数据类型是在实现该接口的时候指定的。
 	2. 如果一个接口自定义了泛型,在实现该接口的时候没有指定具体的数据类型,那么默认是Object数据类型。 
 
 如果想在创建接口实现类对象的时候再指定接口自定义泛型的具体数据类型该怎么办呢?
 */
interface Dao<T>{	
	public void add(T t);
	public void remove(T t);
}
public class Demo4<T>  implements Dao<T>{
	public static void main(String[] args) {
		new Demo4<String>();
	}
	@Override
	public void add(T t) {
		// TODO Auto-generated method stub
	}
	@Override
	public void remove(T t) {
		// TODO Auto-generated method stub
	}
}

  1. IO流
    IO流的分类
package cn.itcast.io;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
/*
IO解决问题: 解决设备与设备之间 的数据传输问题。  比如: 硬盘--->内存            内存----->硬盘
字节流:
输入字节流:
---------| InputStream 所有输入字节流的基类。 抽象类。
------------| FileInputStream 读取文件的输入字节流。
------------| BufferedInputStream 缓冲输入字节流。 该类内部其实就是维护了一个8kb字节数组而已。  该类出现的目的是为了提高读取文件数据的效率。

输出字节流:
---------| OutputStream  所有输出字节流的基类。  抽象类。
------------| FileOutputStream 向文件输出数据 的输出字节流。
------------| BufferedOutputStream  缓冲输出字节流。 该类出现的目的是为了提高向文件写数据的效率。 该类内部其实也是维护了一个8kb的字节数组而已。

什么情况使用字节流: 读取到数据不需要经过编码或者解码的情况情况下这时候使用字节流。比如:图片数据
字符流 = 字节流 + 编码(解码)
字符流:
输入字符流
--------| Reader 所有输入字符流的基类。 抽象类。
-----------| FileReader 读取文件字符的输入字符流。 
-----------| BufferedReader 缓冲输入字符流。 该类出现的目的是为了提高读取文件字符的效率并且拓展了功能(readLine()),它内部 其实就是维护了一个8192个长度的字符数组。

输出字符流
-------| Writer 所有输出字符流的基类。 抽象类。
------------| FileWriter 向文件输出字符数据的输出字符流。
---------------| BufferedWriter 缓冲输出字符流。该类出现的目的是为了提高写文件字符的效率并且拓展了功能(newLine())。

什么情况下使用字符流:如果读写的都是字符数据,这时候我们就使用字符流。
转换流:
输入字节流的转换流             输入字节流---------输入字符流
	InputSrteamReader 
输出字节流的转换流
	OutputStreamWriter

转换流的作用:
	1. 可以把对应的字节流转换成字符流使用。
	2. 可以指定码表进行读写文件的数据。
FileReader, FileWriter这两个类默认是使用的是gbk编码 表。不能由你来指定码表读写文件数据。
 */
public class Demo1 {
	public static void main(String[] args) throws Exception {
//		testInput();
//		writeFile();
		readFile();
	}
	public static void readFile() throws IOException{
		//建立文件与程序的输入数据通道
		FileInputStream fileInputStream = new FileInputStream("F:\\a.txt");
		//创建输入字节流的转换流并且指定码表进行读取
		InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
		int content = 0; 
		while((content = inputStreamReader.read())!=-1){
			System.out.println((char)content);
		}
		//关闭资源
		inputStreamReader.close();
		/*FileReader fileReader = new FileReader("F:\\a.txt");  //默认使用的是gbk码表
		int content = 0; 
		while((content = fileReader.read())!=-1){
			System.out.print((char)content);
		}
		//关闭资源
		fileReader.close();*/	
	}
	//指定使用utf-8码表把数据写出到文件上。
	public static void writeFile() throws IOException{
		//建立了文件与程序的数据 通道
		FileOutputStream fileOutputStream = new FileOutputStream("F:\\a.txt");
		//创建一个输出字节流的转换流并且指定码表进行写数据
		OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");
		outputStreamWriter.write("大家好");  //中文在utf-8码表中占三个字节。	
		//关闭资源
		outputStreamWriter.close();
	}
	public static void testOutput() throws Exception{
		Socket socket = new Socket(InetAddress.getLocalHost(),9090);
		//获取到socket的输出流对象。
		OutputStream outputStream = 	socket.getOutputStream();
		//把输出字节流转换成输出字符流
		OutputStreamWriter  outputStreamWriter = new OutputStreamWriter(outputStream);
		outputStreamWriter.write("不饿!");
	}
	public static void testInput() throws Exception{
		InputStream in = System.in; 
//		int  content = in.read(); // 每次只会读取到一个字节的数据
		//需要把字节流转换成字符流使用。
		InputStreamReader  inputStreamReader = new InputStreamReader(in);
		BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
		System.out.println(bufferedReader.readLine());
	}	
}

4.多线程

启动线程的方式
1.自定义的类继承Thread类。
使用代码为new MyThread().start()
2.自定义的类实现Runnable接口。
使用代码为new Thread(new MyRunnable()).start

package cn.itcast.thread;
/*
线程:

多线程的好处: 多线程解决了在一个进程中同时可以执行多个任务代码的问题。
自定义线程的创建方式:

	方式一:继承Thread.
		1. 自定义一个类继承Thread类。
		2. 重写Thread的run方法,把自定义线程的任务代码定义在run方法上。
		3. 创建Thread子类的对象,并且调用start方法启动一个线程。

	
	方式二: 实现Runnable接口。
		1. 自定义一个类实现Runnable接口。
		2. 实现Runnable接口中的run方法,把自定义线程的任务代码定义在run方法上。
		3. 创建Runable实现类 的对象。
		4. 创建Thread对象,并且把Runnable实现类的对象作为参数传递。
		5. 调用Thread对象的start方法开启线程。
		
线程安全 问题的解决方案:

线程安全问题出现 的根本原因:
	1. 必须要存在两个或者两个以上的线程共享着一个资源。
	2. 操作共享资源的代码必须有两句或者两句以上。

	1. 同步代码块
		synchronized(锁){
			需要被同步的代码
		}
	2. 同步函数。
		修饰符 synchronized 返回值类型   函数名(形参列表..){
		
		}
	注意:
		1. 同步代码块的锁可以是任意的对象。 同步函数的锁是固定 的,非静态函数的锁对象是this对象。 静态函数的锁对象是class对象。
		2. 锁对象必须是多线程共享的对象,否则锁不住。
		3. 在同步代码块或者是同步函数中调用sleep方法是不会释放锁对象的,如果是调用了wait方法是会释放锁对象的。
 */

public class Demo1 extends Thread {

	public Demo1(String name){
		super(name);
	}
	
	@Override
	public void run() {
		for(int i = 0 ; i< 100 ; i++){
			System.out.println(Thread.currentThread().getName()+":"+ i);
		}
	}
	
	public static void main(String[] args) {
		Demo1 d = new Demo1("狗娃");
		d.start(); //开启线程。 线程一旦开启就会指定run方法中 的代码。
		for(int i = 0 ; i< 100 ; i++){
			System.out.println(Thread.currentThread().getName()+":"+ i);
		}
	}
}

二、Junit单元测试

1.Junit单元测试框架的基本使用

一、搭建环境:
导入junit.jar包(junit4)
二、写测试类:
0,一般一个类对应一个测试类。
1,测试类与被测试类最好是放到同一个包中(可以是不同的源文件夹)
2,测试类的名字为被测试类的名字加Test后缀。
三:写测试方法:
0,一般一个方法对应一个单元测试方法。
1,测试方法的名字为test前缀加被测试方法的名字,如testAddPerson()。
2,单元测试方法上面要加上@Test注解(org.junit.Test)!
3,单元测试方法不能有参数,也不能有返回值(返回void)!测试的方法不能是静态的方法。
四、测试方法的基本使用:
1,可以单独执行一个测试方法,也可以一次执行所有的、一个包的、一个类中所有的测试方法。
2,执行完后,显示绿色表示测试成功;显示红色表示测试失败(抛异常后会测试失败)。

package cn.itcsat.junit;

import static org.junit.Assert.*;

import java.util.Arrays;

import javax.management.RuntimeErrorException;

import org.junit.Test;

/*
junit(单元测试框架)

目前存在的问题:
	1. 目前的方法如果需要测试,都需要在main方法上调用。
	2. 目前的结果都需要我们人工对比。


junit要注意的细节:
	1. 如果使用junit测试一个方法的时候,在junit窗口上显示绿条那么代表测试正确,
		如果是出现了红条,则代表该方法测试出现了异常不通过。
	2. 如果点击方法名、 类名、包名、 工程名运行junit分别测试的是对应的方法,类、 包中 的所有类的test方法,工程中的所有test方法。
	3.  @Test测试的方法不能是static修饰与不能带有形参。
	4. 如果测试一个方法的时候需要准备测试的环境或者是清理测试的环境,那么可以@Before、 @After 、@BeforeClass、 @AfterClass这四个注解。
	@Before、 @After 是在每个测试方法测试的时候都会调用一次, @BeforeClass、 @AfterClass是在所有的测试方法测试之前与测试之后调用一次而已。

junit使用规范:
	1. 一个类如果需要测试,那么该类就应该对应着一个测试类,测试类的命名规范 : 被测试类的类名+ Test.
	2. 一个被测试的方法一般对应着一个测试的方法,测试的方法的命名规范是: test+ 被测试的方法的方法名

 */
public class Demo1 {
	
	@Test //注解
	public	 void getMax(int a, int b){
	/*	int a = 3;
		int b = 5 ;*/
		int max = a>b?a:b;
		System.out.println("最大值:"+max);
	}

	
	@Test
	public void sort(){
		int[] arr = {12,4,1,19};
		for(int i = 0 ; i  < arr.length-1 ; i++){
			for(int j = i+1 ; j<arr.length ; j++){
				if(arr[i]>arr[j]){
					int temp = arr[i];
					arr[i] = arr[j];
					arr[j] = temp;
				}
			}
		}
		
		System.out.println("数组的元素:"+Arrays.toString(arr));
		
	}
	

}

2.用于准备环境、清理环境的方法

@Test
表示单元测试方法。
@Before
所修饰的方法应是非static的(且没有参数,返回值为void)。
表示这个方法会在本类中的每个单元测试方法之前都执行一次。
@After
所修饰的方法应是非static的(且没有参数,返回值为void)。
表示这个方法会在本类中的每个单元测试方法之后都执行一次。
@BeforeClass
所修饰的方法应是static的(且没有参数,返回值为void)。
表示这个方法会在本类中的所有单元测试方法之前执行,只执行一次。
@AfterClass
所修饰的方法应是static的(且没有参数,返回值为void)。
表示这个方法会在本类中的所有单元测试方法之后执行,只执行一次。

package cn.itcsat.junit;

import java.io.FileInputStream;
import java.io.IOException;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class Demo2 {
	
	//准备测试的环境
	//@Before
	@BeforeClass
	public static void beforeRead(){
		System.out.println("准备测试环境成功...");
	}
	
	
	//读取文件数据,把把文件数据都
	@Test
	public void readFile() throws IOException{
		FileInputStream fileInputStream = new FileInputStream("F:\\a.txt");
		int content  = fileInputStream.read();
		System.out.println("内容:"+content);
	}
	
	
	@Test
	public void sort(){
		System.out.println("读取文件数据排序..");
	}

	
	//清理测试环境的方法
//	@After 
	@AfterClass
	public static void afterRead(){
		System.out.println("清理测试环境..");
	}
	

}

  1. Assert断言工具类

其中有一些静态的工具方法(不符合期望就抛异常):

assertTrue(...)		参数的值应是true
assertFalse(...)	参数的值应是false  

assertNull(...)		应是null值
assertNotNull(...)	应是非null的值

assertSame(...)		使用==比较的结果为true(表示同一个对象)
AssertNotSame(...)	使用==比较的结果为false

assertEquals(...)	两个对象equals()方法比较结果为true
package cn.itcsat.junit;

public class Tool {
	
	public static int getMax(){
		int a = 3;
		int b  =5; 
		int max = a>b?a:b;
		return max;
	}
	
	public static int getMin(){
		int a = 3;
		int b = 5; 
		int min = a<b?a:b;
		return min;
	}

}

package cn.itcsat.junit;

import junit.framework.Assert;

import org.junit.Test;

//测试类
public class ToolTest {
	
	@Test
	public void testGetMax(){
		int max = Tool.getMax();
		if(max!=5){
			throw new RuntimeException();
		}else{
			System.out.println("最大值:"+ max);
		}
		
		//断言
		//Assert.assertSame(5, max); // expected 期望   actual  真实     ==
//		Assert.assertSame(new String("abc"), "abc");
//		Assert.assertEquals(new String("abc"), "abc"); //底层是使用Equals方法比较的
//		Assert.assertNull("aa");
//		Assert.assertTrue(true);
		
		
		
	}
	
	@Test
	public void  testGetMin(){
		int min = Tool.getMin(); 
		if(min!=3){
			throw new RuntimeException();
		}else{
			System.out.println("最小值:"+ min);
		}
	}
}

三、内省(Introspector)

为什么要学内省?
开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性。

内省是用于操作java对象的属性的,那么以下问题我们必须要清楚。

问题一: 什么是Java对象的属性和属性的读写方法?

问题二: 如何通过内省访问到javaBean的属性 ?

1. 通过PropertyDescriptor类操作Bean的属性.
	
public static void testPropertyDescriptor() throws Exception{
		Person p = new Person();
		PropertyDescriptor propertyDescriptor =  new PropertyDescriptor("id",Person.class);
		//获取属性的写的方法。
		Method writeMethod = propertyDescriptor.getWriteMethod();
		Method readMethod = propertyDescriptor.getReadMethod();
		propertyDescriptor.getReadMethod();
		writeMethod.invoke(p, 12);
		System.out.println(readMethod.invoke(p, null));
	}


2. 通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后通过反射机制来调用这些方法。

public static void testIntrospector() throws Exception{
		BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
		PropertyDescriptor[]  descriptor = beanInfo.getPropertyDescriptors();
		for(PropertyDescriptor itemProperty : descriptor){
			System.out.println(itemProperty.getReadMethod().getName());
		}
	}

存在的问题: sun公司的内省API过于繁琐,所以Apache组织结合很多实际开发中的应用场景开发了一套简单、易用的API操作Bean的属性——BeanUtilspublic static void main(String[] args) throws Exception {
		Person p = new Person();
		ConvertUtils.register(new Converter() {
			
			@Override
			public Object convert(Class type, Object value) {
				 try {
					if(value!=null){
						 
						 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM dd");
						 Date d = dateFormat.parse((String) value);
						 return d;
					 }
				} catch (ParseException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				return null;
			}
		}, Date.class);
		
		BeanUtils.setProperty(p,"id","110");
		BeanUtils.setProperty(p,"name","狗娃");
		BeanUtils.setProperty(p, "birthDay","1992 12 12");
		System.out.println(p.getId() +"=="+ p.getName()+"======"+p.getBirthDay());
	}

package cn.itcast.introspector;

import java.io.BufferedReader;
/*
需求: 编写一个工厂方法根据配置文件的内容,工厂方法返回对应的对象,并且把对象要有对应的属性值。
 */
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/*
 
 以后我们开发框架 的时候,我们是经常需要把一些数据封装到对象中的。
 
 
 
 */


public class Demo1 {
	
	public static void main(String[] args) throws Exception {
		Person p = (Person) getInstance();
		System.out.println(p);
		
	}
	
	//根据配置文件的内容生产对象的对象并且要把对象的属性值封装到对象中。
	public static Object getInstance() throws Exception{
		BufferedReader bufferedReader = new BufferedReader(new FileReader("obj.txt"));
		String className =  bufferedReader.readLine(); //读取配置文件获取到完整的类名。
		Class clazz = Class.forName(className);
		//通过class对象获取到无参的构造方法
		Constructor constructor = clazz.getConstructor(null);
		//创建对象
		Object o  = constructor.newInstance(null);
		//读取属性值
		String line = null;
		while((line = bufferedReader.readLine())!=null){
			String[] datas = line.split("=");
			//通过属性名获取到对应的Field对象。
			Field field = 	clazz.getDeclaredField(datas[0]);
			if(field.getType()==int.class){
				field.set(o, Integer.parseInt(datas[1]));
			}else{
				field.set(o, datas[1]);
			}
			
		}
		return o;
		
	}

}

package cn.itcast.introspector;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import org.junit.Test;

/*
内省--->一个变态的反射.

内省主要解决 的问题: 把对象的属性数据封装 到对象中。


 */
public class Demo2 {
	
	
	@Test
	public void getAllProperty() throws IntrospectionException{
		//Introspector 内省类
		BeanInfo beanInfo = Introspector.getBeanInfo(Person.class);
		//通过BeanInfo获取所有的属性描述其
		PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); //获取一个类中的所有属性描述器
		for(PropertyDescriptor p : descriptors){
			System.out.println(p.getReadMethod()); //get方法
		}
		
		
	}
	
	
	@Test
	public  void testProperty() throws Exception{
		Person p = new Person();
		//属性描述器
		PropertyDescriptor descriptor = new PropertyDescriptor("id", Person.class);
		//获取属性对应的get或者是set方法设置或者获取属性了。
		Method  m = descriptor.getWriteMethod();  //获取属性的set方法。
		//执行该方法设置属性值
		m.invoke(p,110);
		
		Method readMethod = descriptor.getReadMethod(); //是获取属性的get方法
		
		System.out.println(readMethod.invoke(p, null));
	}

}

package cn.itcast.introspector;

import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.xml.crypto.Data;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;

/*
 BeanUtils:
 
 BeanUtils主要解决 的问题: 把对象的属性数据封装 到对象中。
 
 BeanUtils的好处:
 	1. BeanUtils设置属性值的时候,如果属性是基本数据 类型,BeanUtils会自动帮我转换数据类型。
  	2. BeanUtils设置属性值的时候底层也是依赖于get或者Set方法设置以及获取属性值的。
  	3. BeanUtils设置属性值,如果设置的属性是其他的引用 类型数据,那么这时候必须要注册一个类型转换器。
  	
 
 

 BeanUtilss使用的步骤:
 	1. 导包commons-logging.jar 、 commons-beanutils-1.8.0.jar

 */
public class Demo3 {

	public static void main(String[] args) throws Exception {
		//从文件中读取到的数据都是字符串的数据,或者是表单提交的数据获取到的时候也是字符串的数据。
		String id ="110";
		String name="陈其";
		String salary = "1000.0";
		String birthday = "2013-12-10";
		
		
		//注册一个类型转换器
		ConvertUtils.register(new Converter() {

			@Override
			public Object convert(Class type, Object value) { // type : 目前所遇到的数据类型。  value :目前参数的值。
				Date date = null;
				try{
					SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
					date = dateFormat.parse((String)value);
				}catch(Exception e){
					e.printStackTrace();
				}
				return date;
			}
			
		}, Date.class);
		
		
		Emp  e = new Emp();
		BeanUtils.setProperty(e, "id", id);
		BeanUtils.setProperty(e, "name",name);
		BeanUtils.setProperty(e, "salary",salary);
		BeanUtils.setProperty(e, "birthday",birthday);
		
		
		System.out.println(e);
		
		
		
		
	}
	
}

package cn.itcast.introspector;

import java.util.Date;

public class Emp {
		
	private  int id;
	
	private String name;
	
	private double salary;
	
	private Date birthday;


	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public Emp(int id, String name, double salary) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	public Emp(){}
	
	
	@Override
	public String toString() {
		return "编号:"+this.id+" 姓名:"+ this.name+ " 薪水:"+ this.salary+" 生日:"+ birthday;
	}

}

package cn.itcast.introspector;

//实体类---javaBean
public class Person {
	
	private int id;
	
	private String name;

	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	
	public Person(){}
	
	
	
	public int getId() {
		return id;
	}


	public void setId(int id) {
		this.id = id;
	}


	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}


	@Override
	public String toString() {
	
		return "编号:"+ this.id+" 姓名:"+ this.name;
	}

}

四、Properties类与配置文件

Properties配置文件说明
Properties类对应.properties文件。文件内容是键值对,键值对之间使用"="或空格隔开。开头是"#"的表示注释

	Properties类在加载.properties文件时使用的iso8859-1的编码。所以这个文件中的中文要特殊处理:如果这个配置文件中有中文就必须要进行转义,使用native2ascii.exe命令操作:
native2ascii d:/my.properties d:/my2.properties
	
	使用Properties类中的load(InputStream) 方法可以加载配置文件,使用其中的store(OutputStream) 方法可以保存配置到指定文件。

	更多的信息可以看Properties类的API文档。

加载配置文件
 public static void testLoadProperties() throws Exception {
	Properties properties = new Properties();

	InputStream in = new FileInputStream("E:/itcast/config.properties");
	properties.load(in); // 加载
	in.close();

	System.out.println(properties);
写配置文件
 public static void testStoreProperties() throws Exception {
	// 准备配置信息
	Properties properties = new Properties();
	properties.setProperty("name", "李四");
	properties.setProperty("age", "20");

	// 准备
	OutputStream out = new FileOutputStream("d:/my.properties");
	String comments = "这是我的配置文件";

	// 写出去
	properties.store(out, comments);
	out.close();
}



使用Propertiespublic class DBUtil {
	
	static Properties properties = new Properties();
	
	static{
		try {
			Class clazz = DBUtil.class;
			InputStreamReader fileReader =
			new InputStreamReader(clazz.getResourceAsStream("/db.properties"));
			properties.load(fileReader);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static String getUserName(){
		String userName =properties.getProperty("userName");
		return userName;
	}
	
	public static String getPassword(){
		return	properties.getProperty("password");
	}
	public static void main(String[] args) {
		System.out.println("用户名:"+ getUserName());
		System.out.println("密码: "+  getPassword());
	}
}

文件

五、文件路径

绝对路径
以根目录或某盘符开头的路径(或者说完整的路径)
例如:
c:/a.txt (Windows操作系统中)
c:/xxx/a.txt (Windows操作系统中)
/var/xx/aa.txt (Linux操作系统中)

绝对路径的问题: 比如C:\abc\a.properties文件路径,该路径在windows上执行没有 问题,但是如果把该项目移动到linux上面执行 ,该路径就会出现问题了,因为在linux上面没有c盘的,只有根目录\。 

相对路径
相对于当前路径的一个路径。例如当前文件夹为c:/abc时:相对路径a.txt表示c:/abc/a.txt,相对路径xx/a.txt = c:/abc/xx/a.txt
		
.  表示当前文件夹
.. 表示上级文件夹	

相对路径存在的问题:相对路径是相对于目前执行class文件的时候,控制台所在的路径,这样子也会导致出现问题。

Java程序中的相对路径
在Java程序中使用File时写相对路径,是指相对于执行java命令时当前所在的文件夹。
public class PathTest {
	public static void main(String[] args) throws Exception {
		System.out.println(new File("a.txt").getAbsolutePath());
	}
}
在命令行中使用cd命令切换到不同的路径下试试,可以看到以上所说的效果。

在Eclipse中,当前路径是工程的根目录。
classpath路径
classpath路径说明
在Java程序中,一般情况下使用绝对路径还是相对路径都不太合适,因为Java程序的jar包所放的位置不确定,执行java程序时当前的路径也不确定,所以不合适。一般在Java程序中我们会把资源放到classpath中,然后使用classpath路径查找资源。

Classpath路径:就是使用classpath目前的路径。
获取classpath中的资源(InputStreampublic static void main(String[] args) throws Exception {
	Class clazz = new ClassPathTest().getClass();
	
	// 开头的'/'表示classpath的根目录,这个是表示从classpath的根目录中开始查找资源
	InputStream in = clazz.getResourceAsStream("/cn/itcast/my.properties");

	// 如果开头没有'/',表示从当前这个class所在的包中开始查找
	InputStream in2 = clazz.getResourceAsStream("my.properties");
}




package cn.itcast.path;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/*
 如果经常会发生变化的数据我们可以定义在配置文件上。 比如说:数据库的用户名与密码。
  
 配置文件的路径应该如何写 呢?
 	
 	绝对路径:一个文件的完整路径信息。一般绝对路径是包含有盘符 的。  绝对路径的缺陷: 因为绝对路径是有盘符开头的,有些系统是没有盘符的。
 	
 	相对路径: 相对路径是相对于当前程序的路径。当前路径就是执行java命令的时候,控制台所在的路径。
 	
 	类文件路径 :类文件路径就是使用了classpath的路径找对应的资源文件。
 	
 	如果需要使用到类文件路径首先先要获取到一个Class对象。
 	
 
 
 */
public class DBUtil {
	
	static Properties properties ;
	
	static{
		try {
			properties = new Properties();
			//去加载配置文件  /
			Class clazz = DBUtil.class; 
			InputStream inputStream = clazz.getResourceAsStream("/db.properties"); //  "/"代表了Classpath的路径。           getResourceAsStream 该方法里面使用的路径就是使用了类文件路径。
			properties.load(inputStream);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		System.out.println("当前路径:"+ new File(".").getAbsolutePath() );
		System.out.println("用户名:"+ properties.getProperty("userName")+" 密码:"+properties.getProperty("password"));
		
	}
	
	

}

package cn.itcast.path;
/*
 以后的数据库的用户名与密码一般都会 保存到properties(配置文件)
 
 */
public class Demo1 {
	
	//硬编码
	static	String userName = "root";
	
	static String password = "123";
	
	public static void main(String[] args) {
		System.out.println("用户名:"+ userName+" 密码:"+password);
	}
	
	

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值