第1天(就业班) 集合、泛型、io、多线程、Junit单元测试、内省、Beanutils、路径

时间:2022-12-17 13:15:47

1.集合

package cn.xp.list;
import java.util.HashSet;

/**
*1. 集合:存储对象数据的集合容器
* 单列集合
* -----!Collection 单列集合的根接口
|List 如果是实现了list接口的集合类具备的特点 :有序,可重复
|ArrayList ArrayList底层是使用了object数组实现的,特点是:查询速度快,增删慢,内存地址是连续的,
|LinkList 底层是使用了链表数据节后结构的特点是:查询速度慢,增删速度快
|Vector 实现原理与ArrayList是一一致的,但是线程是安全的,操作效率低
|Set 如果是实现了Set接口的集合类 具备的特点是:无序,不可重复
HashSet 底层是使用了哈希表实现的。特点是:存储速度快
*HashSet存储元素的原理:
* 往HashSet添加元素的时候,首先会调用元素的hashcode方法得到元素的哈希码值,然后把哈希码值经过运算算出该元素在哈希表中的位置
* 情况1:如果算出的位置目前已经还没有存在任何的元素,那么该元素可以直接添加到哈希表中
* 情况2:如果算出的位置目前已经存在其他的元素,那么还会调用元素的equals方法再与这个位置上的元素比较一次
* 如果equals方法返回值的是true,那么该元素被视为重复元素,不允许添加,反之,那么该元素可以被添加
* TreeSet 底层是使用了(红黑树)二叉树数据结构实现的,特点是:对集合中的元素进行排序存储
*TreeSet 要注意的一些事项
* 1.往TreeSet添加元素的时候,如果元素具备自然顺序的特点,那么TreeSet会根据元素的自然顺序特性进行存储
* 2.往TreeSet添加元素的时候,如果元素不具备自然顺序的特点,那么元素所属的类必须要实现comparable接口,把比较规则的定义在compareto方法上
* 3.往TreeSet添加元素的时候,如果元素不具备自然顺序的特点,那么元素所属的类也没有实现comparable接口,那么在创建TreeSet对象的时候必须要传入比较器对象
* 比较器的定义格式:
* class 类名 implements Comparator{
*
* }
* 双列集合
* Map 存储的数据都是以键值对形式存在的,键不刻意不重复,值可重复
* HashMap 底层也是使用了哈希表实现的
* TreeMap 底层也是使用了红黑树数据结构实现的
*/
class Person {
int id;
String name;

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

@Override
public int hashCode() {
return this.id;
}

@Override
public boolean equals(Object obj) {
Person person = (Person) obj;

return this.id == person.id;
}

@Override
public String toString() {
return "编号" + this.id + ", 性别" + this.name;
}
}


public class Demo1 {
public static void main(String[] args) {
HashSet<Person> set = new HashSet<Person>();
set.add(new Person(110, "keke"));
set.add(new Person(110, "keke"));
System.out.println("集合的元素:" + set);
}
}
2.集合的练习

package cn.xp.list;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;


public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
// 使用get方法遍历。
list.add("张三");
list.add("李四");
list.add("王五");

System.out.println("======get方式遍历=========");

for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + ",");
}

// 使用迭代器 注意: 迭代器在迭代的 过程中不能使用集合对象修改集合中的元素个数。如果需要修改要使用迭代器的方法进行修改,
System.out.println("\r\n======迭代器方式遍历=========");

HashSet<String> set = new HashSet<String>();
set.add("狗娃");
set.add("狗剩");
set.add("铁蛋");

/*
* Iterator<String> it = set.iterator(); //获取到迭代器 while(it.hasNext()){
* System.out.print(it.next()+","); }
*/
for (String item : set) {
System.out.print(item + ",");
}

System.out.println("\r\n======entrySet方式遍历=========");

HashMap<String, String> map = new HashMap<String, String>();
map.put("张三", "001");
map.put("李四", "002");
map.put("王五", "003");

Set<Entry<String, String>> entrys = map.entrySet(); //

for (Entry<String, String> entry : entrys) {
System.out.println("键:" + entry.getKey() + " 值:" +
entry.getValue());
}
}
}

3.泛型

package cn.xp.genecity;
import java.util.ArrayList;
/*
泛型 的好处:
1. 把运行时出现 的问题提前至了编译时。
2. 避免了无谓的强制类型转换。
泛型在集合中的应用:
ArrayList<String> list = new ArrayList<String>(); true true true
ArrayList<Object> list = new ArrayList<String>(); true true false
ArrayList<String> list = new ArrayList<Object>(); false true false
考虑到新老系统兼用性:
ArrayList list = new ArrayList<String>(); false false true
ArrayList<String> list = new ArrayList(); true false false
注意: 在泛型中没有多态的概念,两边的数据必须要一致。 或者是只写一边 的泛型类型。
推荐使用: 两边的数据类型都写上一致的。
*/
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.xp.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.xp.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) {
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.xp.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) {
}

@Override
public void remove(T t) {

}
}

4.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());
}
}
多线程
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);
}
}
}
5.Junit单元测试框架

package cn.xp.junit;
import java.util.Arrays;

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));
}
}
package cn.xp.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.xp.junit;

import junit.framework.Assert;

import org.junit.Test;


//测试类
@SuppressWarnings("deprecation")
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);
}
}
}

package cn.xp.junit;

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

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


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("清理测试环境..");
}
}
6.内省、Beanutils

import java.io.BufferedReader;

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

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

/*
以后我们开发框架 的时候,我们是经常需要把一些数据封装到对象中的。
*/
package cn.xp.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;
}
}
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;
}
}

import org.junit.Test;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/*
内省--->一个变态的反射.
内省主要解决 的问题: 把对象的属性数据封装 到对象中。
*/
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.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.xp.introspector;

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

import java.text.SimpleDateFormat;

import java.util.Date;

/*
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);
}
}
7.路径问题

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

package cn.xp.path;

import java.io.File;
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"));

}
}