个人笔记——第二阶段javaEE学习

?DAY01—collection-----------19-05-13-Mon--------------

?1. 接口相关内容复习

1.1 collection接口
关键词❓:List / ArrayList / LinkedList;Set / HashSet / TreeSet;Map / HashMap
Collection接口:
--|	List 接口: 元素是有序的,可以重复的;
	--| ArrayList: 是由数组实现的(底层是一个数组),是由一个初始长度为10的数组组成的
		     特点: 增删慢,查找快
				  初始容量可以自定义: 
									new ArrayList(100);	这样100就是当前容量
									
				  List集合是存储在数组中的,而数组是存储在JVM中的,JVM存储在内存中
				  JVM在运行时默认分配内存空间是215M,一般建议修改内存的容量。
				  
				  //1. 什么时候扩容??
				  	扩容方法在add方法中,如果新增一个元素,长度大于当前数组的长度,就开始扩容!
				  //2. 扩大多少?
				  	每次扩大为原来的1.5--| LinkedList: (用的不多,面试) 底层是一个链表
		      特点: 增删快,查找慢
		      		
				  	
--| Set	接口:	元素是无序的,不可以重复!!
	--| HashSet 底层存储结构是一个hash表,存储效率极高
	
			Set<String> set =new HashSet<String>();

-------------HashSet的去重原理:
					HashSet在存储值的时候,会先获取该对象的HashCode值,经过Hash算法,算出该对象在内					 存中的位置;
					如果该位置上有值,那么会进行equals比较
						如果比较结果为相同,不能存储;
						如果不同,可以存储
					如果该位置没值,直接存储
	--| treeSet 底层存储结构是一个树形结构,存储需要进行判断比较
			特点:  有序。 compareTo 方法如果是0,说明该元素在集合中已经存在,不添加
				  如果返回正数或者负数是排序规则,正数按照降序排列,负数按照正序排列
Set<String> set3 =new TreeSet<String>();
		set3.add("zhang");
		set3.add("wang");
		set3.add("li");
		set3.add("zhao");
		System.out.println(set3);
		//TreeSet 是有顺序的,默认是按照字典顺序
			
/*
	 * 此处编写排序规则:
	 *   返回值是int类型
	 *   返回整数说明 
	 *     如果返回正数: 倒序排列
	 *           负数 :正序排列
	 *   返回0说明两个元素是相同的
	 */
	/*@Override
	public int compareTo(Student stu) {
		
		return  this.id - stu.getId();
	}*/
MAP: 类似于数据库,号称"本地缓存"----------------->DAY02
    HashMap-->是以键值对的形式存放数据的		key = value
    
1.2 迭代器 Iterator
关键词❓: Iterator、iterable、foreach
Iterable接口 提供了一个方法,可以获取Iterator
Iterator接口 提供的方法:
					hasNext 判断是否有下一个元素
					next 获取当前元素,并指向下一个元素
					remove 删除当前元素 //建议尽量不要在循环中删除元素,这样会导致长度改变造成异常

实现了这个接口,允许对象成为 "foreach" 语句的目标,也就是说,只要一个类是Iterable的子类,都可以使用foreach循环!!!

?2. UUID 全球唯一,不重复

	UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。
	
	UUID由以下几部分的组合:
	(1) 当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
	(2) 时钟序列。
	(3) 全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
	
	UUID是由一组32位数的16进制数字所构成,是故UUID理论上的总数为16^32=2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID,要花100亿年才会将所有UUID用完。
UUID的标准型式包含3216进制数字,以连字号分为五段,形式为8-4-4-4-1232个字符。示例:
550e8400-e29b-41d4-a716-446655440000
     代码示例:
String uuid = UUID.randomUUID().toString();	//其中一个结果为6462cc46-53af-47cf-ad7f-ffcc0ae31d23
uuid = uuid.replace("-", "");	//为了将‘-’去掉
System.out.println(uuid);

?3. 正则表达式常用(部分)

#####关键词❓:\s、\s+、\d、\d+

\s	匹配空格
+	匹配至少一个 eg: \s+ 表示匹配至少一个空格
\d	匹配数字
//注意:在字符串里,用到了反斜杠 "\" ,那么就需要在加上一个反斜杠表示转移字符,即 "\\s+" 这种形式

//接下来这个例子展示了用法
//在一串数字中,有很多空格,我们要匹配多个空格提取数字:

Scanner scanner =new Scanner(System.in);//扫描器
System.out.println("请输入您要相加的数字,用空格隔开:");
String str = scanner.nextLine().trim();//1 5 10     23  12   34,输入这几个数字,随意加空格
String[] arr = str.split("\\s+"); // \s 匹配空格, +  配置至少一个空格
	int sum =0;
	for(String s : arr) {
		if(s.matches("\\d+")) {		//????????????????
			int a = Integer.parseInt(s);	//????????????????
			sum += a;
		}
	}
	System.out.println("sum= "+sum);//

?4.作业

/*
 *  1、编写一个equalsIngoreCase方法
 *  2、trim()方法
 *  3、四位数的验证码生成
 *  4、发牌完
 */
//纯手工制作equalsIngoreCase
	public static boolean equalsIngoreCase(String str01,String str02) {
		
		
		if(str01 == null || str02 == null) {
			return false;
		}
		
		if(str01.length() != str02.length()) {
			return false;
		}
			
		char[] array01 = str01.toLowerCase().toCharArray();
		char[] array02 = str02.toLowerCase().toCharArray();
	    for(int i=0;i<array01.length;i++) {
	    	if(array01[i] != array02[i]) {
	    		return false;
	    	}
	    }
	    return true;
		
	}
//2 trim()方法
public static String trim(String oldStr) {
		// "    nihao tom   "
		char[] charArray = oldStr.toCharArray();//''
		int begin = 0;
		int end = oldStr.length() -1;
		for(int i =0;i< charArray.length ;i++) {
			if(charArray[i] != ' ') {
				begin = i;
				break;
			}
		}
		
		for(int i =end;i >= 0 ;i--) {
			if(charArray[i] != ' ') {
				end = i;
				break;
			}
		}
		
		// 前包后不包  [)
		return oldStr.substring(begin, end+1);
		
	}

?DAY02–MAP------------------19-05-14-Tue------------

####?1.MAP——号称“本地缓存”

	所有的容器:集合、数组,都是放在内存中的。MAP是一个单独的接口,注意它并不属于Colection接口
MAP是通过Key-value键值对的形式存储数据一种容器。
/* 本地缓存:Map
 *    硬盘:机械硬盘、固态硬盘
 *    内存:读写速度贼快,但是内存是有限的资源。
 *    
 *  Collection: List Set
 *  Map :
 *     是通过Key-value键值对的形式存储的一种容器
 *     HashMap
 */
 
HashMap<String,String> map =new HashMap<String,String>();
map.put("张三", "Java");
map.put("李四", "Python");
map.put("王五", "H5");
map.put("赵六", "Big Data");

// map集合中key是由set存储的,如果有相同的key,后者会顶替前者
map.put("张三", "Java2");
System.out.println(map.size());
System.out.println(map.get("张三"));

1.1 展示MAP键值对的方式(两种)?
// 先获取key值的set集合,通过key值获取value
Set<String> keySet = map.keySet();//key值是一个set集合
Iterator<String> iterator = keySet.iterator();
while(iterator.hasNext()) {
	String key = iterator.next();
	System.out.println("key="+key+",value="+map.get(key));
}

// 第一种方法的第二个写法  foreach
for (String key : keySet) {
	System.out.println("key="+key+",value="+map.get(key));
}

/*
 * 第二种方法就是:通过map获取Entry对象的集合
 * 然后通过迭代器或者foreach循环这个集合
 */
Set<Entry<String, String>> entrySet = map.entrySet();
Iterator<Entry<String, String>> iterator2 = entrySet.iterator();
while(iterator2.hasNext()) {
	Entry<String, String> entry = iterator2.next();
	System.out.println(entry.getKey()+","+entry.getValue());
}

for (Entry<String, String> entry : entrySet) {
	System.out.println(entry.getKey()+","+entry.getValue());
}
//map集合可以获取所有的value值
Collection<String> values = map.values();
for (String value : values) {
	System.out.println("value="+value);
}
1.2 MAP的方法?
序号 返回值类型 方法 说明
1 void clear( ) 移除所有映射关系
2 set> entrySet( ) 返回此映射中包含的映射关系的Set视图
3 value get(object key) 返回指定键所映射的值
4 int hashCode( ) 返回此映射的hash码值
5 set keySet( ) 返回此映射中包含的所有键的set视图
6 put(key,value) 放入指定键和值
7 putAll(Map m) 类似集合,将一组映射添加到另一组中
8 value remove(object key) 移除指定键的映射关系
9 collection value( ) 返回此映射中值得集合
10 int size( ) 返回键值对数,注意同key则值覆盖
11 boolean containsKey(object key) 若此映射包含指定键的映射关系,返回true
12 boolean containsValue(object value) 指定值被一个或多个键映射,则返回true
13 boolean equals(object obj) 比较指定对象与此映射是否相等
14 boolean isEmpty( ) 该映射没有映射关系,则返回true。注意:若映射 == null,则空指针异常
1.3 Map实例?
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 
 *  商品数据    商品类别
 *  一个商品类别可以有多件商品
 *
 */
public class Demo02 {

	public static void main(String[] args) {
		
		Map<String,List<Goods>> map =new HashMap<String,List<Goods>>();
		
		List<Goods> list01=new ArrayList<Goods>();
		list01.add(new Goods(1,"T Shirt",100));
		list01.add(new Goods(2,"夏威夷风格大裤衩",50));
		map.put("服装", list01);
		
		//精日
		List<Goods> list02=new ArrayList<Goods>();
		list02.add(new Goods(1,"华伪P30 Pro",100));
		list02.add(new Goods(2,"锤子",150));
		map.put("手机", list02);
		
		
		/**
		 * 此处省略10000行代码
		 */
		
		Set<String> keySet = map.keySet();
		for (String key : keySet) {
			System.out.println("此处的商品类型是:"+key);
			System.out.println("有以下商品");
			List<Goods> list = map.get(key);
			for (Goods goods : list) {
				System.out.println(goods);
			}
		}
		
	}

}
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;

/**
 * 
 * 在控制台输出一串字符串,打印每个字符出现的次数
 * 
 */
public class Demo03 {

	public static void main(String[] args) {
		
		Scanner scanner = new Scanner(System.in);
		System.out.println("请输入一串随意的字符串:");
		String line = scanner.nextLine();
		char[] array = line.toCharArray();
		
		Map<Character,Integer> map =new HashMap<Character,Integer>();
		for (char c : array) {
			if(map.containsKey(c)) {
				int count = map.get(c);
				map.put(c, count+1);
			}else {
				map.put(c, 1);
			}
		}
		
		Set<Entry<Character, Integer>> entrySet = map.entrySet();
		for (Entry<Character, Integer> entry : entrySet) {
			System.out.println(entry.getKey()+"出现了"+entry.getValue()+"次");
		}

	}

}

?2. 单元测试

#####关键词❓: @Test、@Before 、@After

@Test	导包用Junit4,不要用5


单元测试用法示例:

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

import com.qfedu.zuoye.StringUtils;

/**
 * 
 * 单元测试Junit:
 * 1、需要导入相应的包  2个
 * 2、编写测试方法的时候只需要在测试方法上方添加@Test即可
 * 3、方法的修饰符必须是public ,返回值必须是void  ,方法名建议以test开头
 * 4、初步体会:可以替代main方法(注意类里面没有main方法的)
 */

public class JunitTest {
	
	/*
	 * 在before注解修饰的方法中经常写一些初始化类的代码
	 */
	@Before
	public void init() {
		
		System.out.println("该方法每一个单元测试方法执行前都必须先执行我!");
		
	}
	
	@Test
	public void testTrim() {
		//int i= 10/0;
		String  str= StringUtils.trim("   abc bcd ddd    ");
		assert (str.equals("abc bcd ddd"));
	}
	
	@Test
	public void testEquals() {
		
		System.out.println(StringUtils.equalsIngoreCase("Abc","abc"));
		
	}
	
	@After
	public void destory() {
		System.out.println("每个单元测试方法执行后,都要执行我");
	}

}

?3. 断言assert

断言,表示预判,若结果与assert表达的一致,则通过

@Test
	public void testAdd() {
		
		int a = computer.add(10, 4);
		assert (a == 10);
	}

?4. 匿名内部类

匿名 --> 没有名字
内部类 --> 类中类,一个类中编写或声明了另一个类

作用:如果一个接口或者一个抽象类的实现类、实例化对象只需要使用一次,不想编写实现类,可以使用匿名内部类来简化这个问题

注意: 匿名内部类只能创建一个对象

?5. 设计模式(部分)

关键词❓: 单例设计模式–>懒汉模式/饿汉模式
单例设计模式:一个类只需要一个实例,不能出现多个实例,就叫单例;

单例模式: 该类只能有一个实例化对象
	1. 私有化构造方法
	2. 编写一个公共方法,供外部调用
	3. 在公共方法中实例化对象

懒汉与饿汉:
 *   懒汉是:创建一个方法,有人使用这个对象的时候再创建,优点是节约内存,缺点是太慢     线程不安全的
 *   饿汉是:先创建对象,别人调用方法直接返回该对象。优点是效率高,缺点是占内存      线程安全
单例模式之 解释 优点 缺点
懒汉模式 创建个方法,有人使用这个对象的时候再创建 节约内存 效率低,线程不安全
饿汉模式 先创建对象,别人调用方法直接返回该对象 效率高,线程安全 占内存
5.1 懒汉模式?
懒汉之"懒":别的类调用该方法的时候采取创建对象
public class Boss {

	private String name;
	
	private static Boss boss =null;

	private Boss(String name ) {
		this.name = name;
	}
	
	public static Boss  instanceBoss() {
		
		if(boss == null) {
			boss =new Boss("王总");	//因为每个类调用的时候要new一个Boss,有可能被其他劫持,不安全
		}
		
		return boss;
	}
}
5.2 饿汉模式?
饿汉之"饿": 先吃饱再说,先创建了再说,不管以后有没有人用!

public class Boss2 {

	private String name;
	
	private static Boss2 boss =new Boss2("王总");

	private Boss2(String name ) {
		this.name = name;
	}
	
	public static Boss2  instanceBoss() {
		
		return boss;
	}
}

?6. collections工具类?

  1. collections.sort(list); 排序
    
  2. collections.reserve(list); 反转元素
    
  3. collections.shuffle(list); 洗牌
    
  4. collections.max(list); 最大值
    
  5. collections.min(list); 最小值
    
  6. index collections.binarySearch(list, element); 二分法查找指定元素的下标 注意:先排序!!
    
  7. collections.replaceAll(list, oldElement, newElement); 将原来指定元素替换为指定元素
    
  8. 插播一条arrays工具类: arrays.aslist(arr); 可以将数组转为list集合,但是该集合是/*只读*/的!!!
    

?7. 作业(递归)

1、递归练习
   2 + 22 + 222 + 2222
   通过递归计算该值是多少?

   2222 = 222*10 + 2222 = 22*10 +2;

?DAY03–File–IO流---------------19-05-15-Wen---------

关键词❓: File、Input、Output

####?1. File 工具类?

File: 就是一个工具类,该类主要用于操作文件及文件夹本身,不对其中的内容及逆行操作
IO流: 对文件中的内容进行写入和读取

eg: 创建一个文件
File file =new File("C:\\Users\\98023\\Desktop\\a.txt");//这里是我们想要创建路径的地方
if(!file.exists()) {	//file.exist()是用于判断一个文件或文件夹是否存在的
	file.createNewFile();
}

eg: 创建一个文件夹
File file2 =new File("\\\\Mac\\Home\\Desktop\\ab\\b");
		//mkdir 和  mkdirs 都可以创建多级目录,如果创建多级目录的话,建议使用mkdirs
file2.mkdir();
1 boolean canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。
2 boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。
3 boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。
4 boolean createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
5 boolean delete() 删除此抽象路径名表示的文件或目录。
6 boolean deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
7 boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
8 File getAbsoluteFile() 返回此抽象路径名的绝对路径名形式。
9 String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
10 String getPath() 将此抽象路径名转换为一个路径名字符串。
11 String getName() 返回由此抽象路径名表示的文件或目录的名称。
12 String getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null
13 File getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null
14 boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。
15 boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。
16 File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
17 boolean mkdir() 创建此抽象路径名指定的目录。
18 boolean mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
19 boolean renameTo(file dest) 重新命名此抽象路径名表示的文件。
20 boolean setReadOnly() 标记此抽象路径名指定的文件或目录,从而只能对其进行读操作。
21 boolean setWriteOnly() 标记此抽象路径名指定的文件或目录,从而只能对其进行写操作。
22 String toString() 返回此抽象路径名的路径名字符串。

?2.Input、Output

IO流:
	向文件中写入数据,读取数据
	MySQL等数据库本身也就是IO流的操作
	
IO流:Input、OutPut  输入输出流

    -->输入,输出 指的是以内存为标志来说
    /*输入流*/:  数据从其他地方流向内存就叫做输入流          读取文件,就是输入流
    /*输出流*/: 从内存中将输入流向其他地方 ,就叫输出流      向文件中写入数据,叫输出流
    
    -->字节流和字符流:
    /*字节流*/,是按照字节byte进行读取或者写入的,一般用于非文本数据的处理,图片,音乐,视频等文件
    		//字节输入/输出流:FileInputStream / FileOutputStream
    /*字符流*/:是按照字符char进行读取和写入的,一般用于文本数据  txt,excel,md,处理汉字比较方便
    		//字符输入/输出流: FileReader / FileWriter  FileWriter参数加true表示不覆盖
2.1 字节输出流?
public static void main(String[] args) {
	
		
		try {
			//字节输出流
			OutputStream outPutStream =  new FileOutputStream(new File("\\\\Mac\\Home\\Desktop\\a.txt"),true);
		    //outPutStream.write("hello world".getBytes());
			outPutStream.write(97);// 97  在ASCII表中表示  a
			byte[] arr = "hello world".getBytes();
			outPutStream.write(arr, 0, 5);
		    outPutStream.flush();//从内存中清除要写的数据到磁盘
		    outPutStream.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
2.2 字节输入流?
字节流不是不能读取汉字,而是一个汉字由 2个或者3个组成
	gbk  是由两个字节表示一个汉字
	utf-8  是由三个字节表示一个汉字

用字节流去读取汉字,有可能出问题,所以为了保险起见,不能使用
一个char 可以表示一个汉字

public static void main(String[] args) {
		
		try {
			// 字节输入流 ,每次读取一个字节
		  InputStream inputStream = 	new FileInputStream(new File("\\\\Mac\\Home\\Desktop\\a.txt"));
		   //int  a = inputStream.read();//每次从文件中读取一个字节,如果遇到汉字,无法读取
		   //System.out.println((char)a);
		  byte[] arr = new byte[10];//定义了一个小车
		  int length = 0; //每次从文件中读取的长度
		  // [a b c d e  f g x x e]
		  //  [b x e d e  f g g x e]
		  //  [c x e d e  f g g x e]
		  
		  /*
		   * inputStream.read(arr)  每次读取arr.length个数据到 arr中
		   * 如果读取不到数据,返回-1,如果读取到数据返回值是读取的数据长度
		   * 每次读取完数据之后,都要将数据组装成一个新的字符串  new String(arr,0,length)
		   */
		  while((length = inputStream.read(arr)) != -1) {
			  System.out.print(new String(arr,0,length));
		  }
		  
		  inputStream.close();
		 
		 // char a = '张';
	  
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
2.3 字符输入流?
字符输入流:
	可以完美读取汉字,妈妈再也不用担心有?号了

public static void main(String[] args) {
		
	try {
		Reader reader = new FileReader(new File("\\\\Mac\\Home\\Desktop\\a.txt"));
	    //int a = reader.read();
	    //System.out.println((char)a);
		char[] arr =new char[10];
		int length = 0;
		while((length = reader.read(arr)) != -1) {
			System.out.print(new String(arr,0,length));
		}
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

}
2.4 字符输出流?
public static void main(String[] args) {

	Writer writer =null;
	try {
		writer = new FileWriter(new File("\\\\Mac\\Home\\Desktop\\a.txt"),true);
	    for(int i =0;i< 10 ;i++) {
	    	writer.write("Java 1903班,真棒!!! \r\n" );
	    }
	    
	    writer.flush();
		
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} finally {
		try {
			if(writer != null) {
				writer.close();
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
2.5 复制图片?
//复制一张图片到另一个地方

public static void main(String[] args) {
	
	InputStream inputStream =null;
	OutputStream outputStream =null;
	try {
		inputStream = new FileInputStream(new File("\\\\Mac\\Home\\Desktop\\a.jpg"));
		
		outputStream = new FileOutputStream(new File("\\\\Mac\\Home\\Desktop\\ab\\aaa.jpg"));
		
		byte[] arr =new byte[1024*20];// 1024 byte = 1kb
		int length = 0;
		while((length= inputStream.read(arr)) != -1) {
			outputStream.write(arr, 0, length);
		}
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} finally {
		try {
			outputStream.close();
			inputStream.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

– 扑克牌例子–>参见H:\qian_phone\课件存放_第二阶段\Day03_IO流\代码\Java03\src\com\qfedu\poker

?DAY04–IO流–各种流------------------19-05-16-Thu------------

关键词:Input(/Output)StreamReader、BufferedInputStream、BufferedReader

?1. 转换流

#####1.1 转换流的作用

转换流 --> InputStreamReader
'转换流' 可以将'字节流'转换为'字符流'
1.2 转换流代码示例?
//字节流
			FileInputStream fileInputStream = new FileInputStream(new File("\\\\Mac\\Home\\Desktop\\a.txt"));
			
			//如果出现读取的内容中文乱码,可以指定字符集,要不然,不要指定,
			// 如何查看文件的字符集     记事本  另存为
			//通过转换流将字节流转换为字符流
			InputStreamReader reader = new InputStreamReader(fileInputStream);
		    char[] arr =new char[50];
			int len = 0;
			while((len = reader.read(arr)) != -1) {
				System.out.print(new String(arr,0,len));
			}

FileOutputStream fileOutputStream = new FileOutputStream(new File("\\\\Mac\\Home\\Desktop\\b.txt"));
		    //此处指定字符集:指定该文件以什么样的字符集创建,并以该字符集写入内容
			//OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream,"UTF-8");
			//如果不指定,走默认  默认是以代码所在环境的字符集为默认字符集
			OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream);
		    for(int i=0;i<10;i++) {
		    	writer.write("这个杀手不太冷"+i+"\r\n");
		    }
		    
		    //一定要记得flush  --> 强制将内存中数据刷入到文件中
		    writer.flush();
		    
		    // OutputStreamWriter 中的close方法,其实就是 调用  fileOutputStream 的close
		    writer.close();

?2. 缓冲流

#####2.1 缓冲流的作用

--> 缓冲流:缓冲流就是套在普通流的外面,起到提升性能的作用,并且可以拓展一些功能

--> BufferedReader 拓展的功能: readLine(),详见2.3
    BufferedWriter 拓展的功能: newLine(),详见2.4
2.2 FileInputStream?
FileInputStream fileInputStream = new FileInputStream(new File("\\\\Mac\\Home\\Desktop\\a.txt"));
BufferedInputStream inputStream = new BufferedInputStream(fileInputStream);

byte[] arr =new byte[10];// 緋豺  
int length = 0;
while((length = inputStream.read(arr)) != -1) {
	System.out.print(new String(arr,0,length));
}

#####2.3 BufferedReader

'BufferedReader': 缓冲字符输入流,主要作用是提升读取速度
				还要一个重要的作用:可以每次读取一行   readLine()
   				 readLine  只读取文字不读取回车或者换行符
				readLine 返回值为null,说明读取到了文件的末尾
BufferedReader reader = new BufferedReader(new FileReader(new File("\\\\Mac\\Home\\Desktop\\a.txt")));
		    String str = null;
			while((str = reader.readLine()) != null) {
		    	System.out.println(str);
		    }
2.4 BufferedWriter?
BufferedWriter writer =null;
		try {
			writer = new BufferedWriter(new FileWriter(new File("\\\\Mac\\Home\\Desktop\\aaa.txt")));
		    for(int i = 0;i < 10;i++) {
		    	writer.write("你好!"+i);
		    	writer.newLine();//新起一行
		    }
		   // writer.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				//BufferedWriter 是套用了 FileWriter ,close方法中包含了数据的写入操作,所以即使writer不flush,数据也会写入磁盘
				//BufferedWriter的close方法,包含了FileWriter的关闭操作
				writer.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

?3. 标准输入输出流

3.1 简单介绍标准流
static PrintStream in 标准输入流;
static PrintStream out 标准输出流;
3.2 代码部分?
public static void main(String[] args) throws Exception {
		
		// PrintStream out  输出流
		
		/*
		 * System 类中常用方法
		 * exit(int status)  status = 0 说明虚拟机正常退出,否则都是异常退出
		 * gc()  垃圾回收
		 */
		
		
		//System.exit(0);//虚拟机正常退出
		System.gc();//给保洁阿姨打电话而已
		long time = System.currentTimeMillis();//可以获取当前时间的一个毫秒值
		// yyyy-MM-dd HH-mm-ss
		Date date = new Date(time);
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss-SSS");
		//System.out.println(format.format(date));
		
		//毫微秒   1秒= 1000毫秒   1毫秒=1000微秒   1微秒= 1000纳秒
		// 毫微秒nanoTime 值
		//System.out.println(System.currentTimeMillis());
		//System.out.println(System.nanoTime());
		// System  是一个工具类
		//System.out.println("Hello World");
		
		//毁三观之一,syso 不一定是输出在控制台的,只是默认输出在控制台而已
		PrintStream printStream = new PrintStream(new File("\\\\Mac\\Home\\Desktop\\console.txt"));
		//System.setOut(printStream);
		//System.out.println("Hello World!");
		
		//毁三观之二,Scanner 数据,不一定都来自于键盘
		InputStream inputStream = new FileInputStream(new File("\\\\Mac\\Home\\Desktop\\scanner.txt"));
		System.setIn(inputStream);
		Scanner scanner =new Scanner(System.in);
		while(scanner.hasNext()) {
			System.out.println(scanner.nextLine());
		}
		
	}

#####3.3 system工具类?

1 static long currentTimeMillis() 返回以毫秒为单位的当前时间。
2 static void exit(int status) 终止当前正在运行的 Java 虚拟机。
3 static void gc() 运行垃圾回收器。
4 static long nanoTime() 返回最准确的可用系统计时器的当前值,以毫微秒为单位。
5 static void setIn(InputStream in) 重新分配“标准”输入流。
6 static void setOut(PrintStream out) 重新分配“标准”输出流。

?4. 对象流

#####4.1 对象流的简单叙述

public static void main(String[] args) throws Exception {
		
	Student stu =new Student("张三",18);
	
	ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("\\\\Mac\\Home\\Desktop\\a.txt")));
	outputStream.writeObject(stu);
	//对象流只能写一个对象进入文件
	//如果想在一个文件中写入多个对象,可以使用容器
	//outputStream.writeObject(new Student("lisi",20));
	outputStream.flush();
	
	outputStream.close();
}
4.2 一种常见的异常——serialVersionUID
private static final long serialVersionUID = 1L;
这句话的意思是,序列化的时候如果没有加上面这句话,也会产生一个serialVersionUID
serialVersionUID 是经过一定的算法算出来的,根据类名,构造方法,参数以及个数等因素计算出来的
如果在此期间修改了某些字段【新增,修改或者删除】,那么UID值就会发生改变
从该文件中读取对象的时候就会对比当前的uid值是否和存入到文件中的uid值是否一样,不一样直接报错

解决办法就是:
  在普通pojo对象中,生成serialVersionUID该值,建议写1L

?5. 装饰者设计模式

#####5.1 装饰者模式定义

装饰者模式:
  其实就是对原有的类进行功能的增强而已
  
  1、要修饰的类和本身的类要实现同样的接口或者父类
  2、装饰类中一定要传递参数,该参数是你要增强的对象
  
  在什么地方使用了装饰者模式?
     IO流中大量使用了装饰者,比如缓存流,转换流,对象流
5.2 装饰者模式示例?
 abstract class AbstractBread {
	
	public abstract void huoMian();
	public abstract void faJiao();
	public abstract void shangLong();
	
	public void make() {
		huoMian();
		faJiao();
		shangLong();
	}

}

 class ColorBread extends AbstractBread {
	
	AbstractBread bread;
	
	public ColorBread(AbstractBread bread) {
		this.bread = bread;
	}
	
	
	public void  printColor() {
		System.out.println("老刘闲赚钱慢,加了一些色素和增白剂!");
	}

	@Override
	public void huoMian() {
		printColor();
		bread.huoMian();
	}

	@Override
	public void faJiao() {
		bread.faJiao();
	}

	@Override
	public void shangLong() {
		bread.shangLong();
	}

}

 class NomalBread extends AbstractBread {

	@Override
	public void huoMian() {
		
		System.out.println("老闫和面,汗流浃背,不用放盐!");
	}

	@Override
	public void faJiao() {
		
		System.out.println("老刘添加一些安琪牌酵母菌进行发酵");
	}

	@Override
	public void shangLong() {
		System.out.println("凯云拿着大锅,上笼");
	}

}

public class Demo {

	public static void main(String[] args) {
	
		AbstractBread nomalBread = new NomalBread();
		nomalBread = new ColorBread(nomalBread);
		
		nomalBread.make();

	}

}

?6.properties

#####6.1 properties

Properties 是Hashtable的子类,而hashtable又实现了Map接口
Properties中数据存储是按照Key - Value键值对存储的
Properties 一般用来写入数据的不多

properties所用的方法:
setProperties(key,value);
store(输入流, 注释); //注释会在文件的开头给出
load(); //加载,将流中的数据加载到properties中
6.2 properties应用举例?
public static void main(String[] args) throws Exception {
	Properties properties =new Properties();
	properties.setProperty("url", "jdbc://mysql://localhost:3306/xiaomi");
	properties.setProperty("driver", "com.mysql.jdbc.Driver");
	
	FileWriter writer = new FileWriter("C:\\Users\\98023\\Desktop\\db.properties");
	properties.store(writer, "这是一个数据库连接文件");
	
	writer.close();
}

---------------------------------------------------------------------------------------------
/*
http://localhost:8080/虚拟路径名【不是项目名】/资源
如何修改虚拟路径名呢?
1、MyEclipse    MyEclipse -->Web Deploy Assemxxxx
2、JavaEE       tomcat -->Modules --> 修改path  【类似于idea】
*/

public void testDb() throws Exception {
	Properties properties =new Properties();
	// 淘汰,测试时没问题,运行时 压根就没有src目录   只要是resource folder 编译完都没有
	//FileInputStream inputStream = new FileInputStream("src/db.properties");
	//从编译之后的classes文件夹下找该文件
	// db.properties 如果在javaee工具下,要加个/
	InputStream inputStream = Demo02.class.getClassLoader().getResourceAsStream("db.properties");
	System.out.println(inputStream);
	properties.load(inputStream);
	System.out.println(properties.getProperty("name"));
	System.out.println(properties.getProperty("password"));
}

public static void main(String[] args) throws Exception {

	Properties properties =new Properties();
	
	/*Reader reader =new FileReader("\\\\Mac\\Home\\Desktop\\db.properties");
	properties.load(reader);
	
	System.out.println(properties.getProperty("url"));
	System.out.println(properties.getProperty("driver"));*/
	
	FileInputStream inputStream = new FileInputStream("src/db.properties");
	properties.load(inputStream);
	System.out.println(properties.getProperty("name"));
	System.out.println(properties.getProperty("password"));
	
}

?DAY05–枚举、反射、注解---------19-05-17-Fri------------

关键词:Enum、reflect、弹性参数、anno

?1. 枚举 Enum

1.1 枚举
枚举,其实是一个类; 枚举类里面可以写普通的方法
也可以写其他的成员变量,'但是一定要写在枚举值的后面',否则会报错

例如在 1.2 的例子中,'SPRING' 等数据,其实都是 'Season类' 的实例化对象

枚举磊,其实就是常量,而且有范围
	一般将一些'常用的数据'写成枚举类,以防止开发人员自己定义变量或者常量,如季节、星期、男女等一些常识性的变量
	
枚举类一般在开发中经常出现在'switch语句'
1.2 枚举示例——一年四季?
public enum Season {

	// public static final Season SPRING=new Season();
	// public static final Season SPRING=new Season("春天");
	
	SPRING("春天"),	//枚举的每一项用逗号','隔开,末尾用分号 ';'
	SUMMER,		//此处相当于"public static final Season SUMMER = new Season" 注意,是相当于
	AUTUMN,
	WINTER;
    
	int a = 10;
	public static final double PI = 3.14;
	
	private String name; //枚举类里可以写其他的成员变量
	
	Season(){
				//也可以写普通的成员方法	
	}
	
	Season(String name) {	//此处是一个有参构造方法,若是没有上面的无参构造,那么前面的枚举会报错
        					//此时,可以给枚举加参数,或者在类中添加无参构造
		this.name= name;
	}
	
	public void print() {
		System.out.println("我是在枚举类中的,我是为了证明某些东西得存在而诞生的");
	}
	
}

-----------枚举的使用-----------------------------------------------------------------
    
public class Demo {

	public static void main(String[] args) {
		
		//switch  后面的内容能跟哪些数据类型  byte,short,int,char,String(jdk1.7之后才支持的),枚举【jdk1.5之后】
		//switch 后面不可跟的数据类型:double,float,boolean,long,
		
		Season season = Season.SUMMER;
		System.out.println(season);
		switch(season) {
		
			case SPRING:
				System.out.println("春天来了,万物复苏,又到了XX的季节!");
				break;
			case SUMMER:
				System.out.println("夏天到了,终于可以撸串了!");
				break;
			case AUTUMN:
				System.out.println("秋天来了,果实晓磊!");
				break;
			case WINTER:
				System.out.println("冬天到了,可以冬眠!");
				break;
			default:
				System.out.println("哥们,数据传递的不对吧!");
				break;
		  
		}
		
		
		Season.AUTUMN.print();	//能用这种方法调用,证明了枚举是实例化对象!!!

	}

}

?2. 反射 reflect

2.1 反射前言
* 'class'类:表示类的类,里面放置了一些 类中共有的一些属性或方法,任何一个普通的类都可以找到该类对应的class* 类 和 对象
	类是对象的一种抽象表示
2.2 什么是反射???
一句话!!!--> 反射就是不实例化对象的情况下,拿到一个类中所有内容的一种手段!!
(反射,是所有框架的灵魂!)

反射的缺点:
	执行效率慢,破坏了我们面向对象的思想
2.3 获取class对象的三种方法
  1. 直接通过类名.class
        Class<?> clazz1 = Demo1.class;
    
  2. 根据类的全路径名称得到class
    Class<?> clazz2 = Class.forName("完整包名+类名");
    
  3. 通过一个类的实例化对象获取该类的class类对象
    Demo1 demo = new Demo1();
    Class<? extends Demo1> clazz3 = demo.getClass();
    
2.4 获取类的内容——获取构造方法、字段、方法
2.4.1 获取类的内容 主体部分?
public class Demo02 {

	public static void main(String[] args) throws Exception {
		
		Student stu =new Student();
		System.out.println(stu.phone);
		stu.getAge();
		
		//第一步:不new就可以获取一个类的实例化对象
		Class<?> clazz = Student.class;
		//获取类中所有的构造方法
		Constructor<?>[] constructors = clazz.getConstructors();
		for (Constructor<?> constructor : constructors) {
			System.out.println(constructor.getName());
		}
		
		//获取Student类中无参数的构造器,得到一个学生对象
		Constructor<?> constructor = clazz.getConstructor();
		Object object = constructor.newInstance();
		Student stu2 = (Student) object;
		System.out.println(stu2);
		
		//通过有参数的构造器得到一个Student对象
		Constructor<?> constructor2 = clazz.getConstructor(int.class,String.class,String.class);
		Object object2 = constructor2.newInstance(12,"张三","181xxxx9903");
		Student stu3 = (Student) object2;
		System.out.println(stu3.getName());
		
		//第二步:可以获取所有字段,包括私有字段
		// 通过getFields 只能获取public字段
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field.getName());
		}
		
		
		// Declared 公开的
		//可以获取所有字段,包含私有的
		Field[] fields2 = clazz.getDeclaredFields();	//getDeclaredFields
		
		for (Field field : fields2) {
			System.out.println(field.getName());
		}
		//第三步:可以调用类中方法,不需要new
		
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println(method.getName());
		}
		
		//调用的是无参数的方法
		Method method = clazz.getDeclaredMethod("getName");
		System.out.println(method.invoke(stu3));
		
		//如果有参数如何调用呢?
		Method method2 = clazz.getDeclaredMethod("setName",String.class);
		method2.invoke(stu2, "李四");
		
		System.out.println(stu2.getName());
		
	}

}
2.4.2 使用到的 Student类?
public class Student {
	
	private int age;
	private String name;
	public String phone;
	
	public Student() {
		
	}
	
	public Student(int age, String name, String phone) {
		
		this.age = age;
		this.name = name;
		this.phone = phone;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getName() {
		return name;
	}

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

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
	

}
2.4.3 获取的总结
获取类中的所有的构造方法:

获取类中所有字段

获取类中的方法

获取注解
2.5 小知识点——弹性参数?
一个方法中,
public void XX(int age, String...args) {
    
}
想获取所有的弹性参数,用args.length,做for循环
注意args是数组,args[i]
//注意:一个方法只允许出现一个弹性参数,单独的参数也可以放,但是必须放到最前面,也就是说,弹性参数必须放到最后面!!!
public class Demo03 {
	
	/*该方法中添加了一个弹性的参数,该参数可以传递多个 0到多个
	   该弹性参数,一个方法只允许出现一个,而且必须放在所有参数的后面
	  String... args 本身就是一个数组,数组如何用,它就怎么用
	*/
	/*public void testParas(String... args,int age) {
		
	}*/
	
	public void  a(String name) {
		
	}
	
    public void  a(String name,String password) {
		
	}
    public void testParas(int age,String... args) {
    	
    	for (String string : args) {
    		System.out.println(string);
		}
    	
    	for (int i = 0; i < args.length; i++) {
			System.out.println(args[i]);
		}
    	
		
	}

	public static void main(String[] args) {
		
		Demo03 demo03 = new Demo03();
		//demo03.testParas(10);
		demo03.testParas(11,"zhangsan","李四","王五");
	}

}

?3. 注解 Anno

3.1 注解的写法?
public @interface 注解名 {
    
}
这是一个自定义注解
一般没有方法体,知识用来标识。
注解:
  1、标识的作用  ,用于让自己或者别的开发人员查看的
  2、注解可以携带信息
3.2 注解的属性?
注解虽然一般没有方法体,但是也可以加入属性
/*
ElementType.METHOD : 可以作用在方法上
            TYPE  : 可以作用在类上
            FIELD : 全局变量
            LOCAL_VARIABLE :局部变量
 
 RetentionPolicy.RUNTIME  定义注解的生命周期
    SOURCE :该注解只在源码期有用,编译成class之后就没有了
    CLASS : 编译之后,该注解还存在,但是运行的时候没有了
    RUNTIME:该注解在什么时候都有
*/
@Target({ElementType.METHOD,ElementType.TYPE}) // 意思是限制该方法只允许出现在哪些地方
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {

	public String name() default "zhangsan";  //如果该字段没有默认值,必须在使用该注解时为其赋值
	public int age() default 10;//如果有默认值,使用的时候就可以不指定该值。
	
	//public String value();//value这个名字很特别,如果该注解中只需要给value赋值,value=   可以省略的,叫其他名字不好使!
    public String[] value();
}

?DAY–06–多线程--------------190-05-20-----------------

关键词:Thread、Runnable、线程安全、synchronized、

?1. 进程?线程?

进程 :正在运行的程序   QQ,百度音乐,eclipse
线程:线程是运行在进程中,一个进程可以包含多个线程  飞Q   收文件,聊天

Java是一个支持多线程的开发语言。

线程的生命周期:
线程调用 start() 方法,该线程不一定立即执行,只是进入到了一个就绪状态
可以抢占资源了。
//main方法是主线程

?2. 线程的写法

2.1 使用extends的写法
1. 编写一个类,继承Thread类
2. 重写run方法	//线程中的所有逻辑,都要写在run方法中
3. 启动子线程,用 start();

public class TalkThread extends Thread{
	
	public TalkThread() {}
	
	//我们先将名字传递给我们自己写的线程,在线程中,通过父类的构造方法,传递给父类
	public TalkThread(String name) {
		super(name);
	}
	
	@Override
	public void run() {
		
		for(int i=0;i<100;i++) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.err.println("我是子线程!"+i);
		}	
	}
	public static void main(String[] args) {
		
		TalkThread  thread =new TalkThread();
		thread.setPriority(Thread.MIN_PRIORITY);	//设置优先级
		//thread.run();
		thread.start();//启动子线程的正确姿势
		
		TalkThread  thread2 =new TalkThread();
		thread2.setPriority(Thread.MAX_PRIORITY);
		thread2.start();
		
		for(int i=0;i<100;i++) {
			System.out.println("我是 主线程:"+i);
		}
	}
}
2.2 使用implements的写法
1、编写一个类实现Runnable,实现run方法
2、实例化该对象,并将该对象,存入Thread的构造方法中
3、thread.start() 启动子线程

class DemoRunnable implements Runnable{

	@Override
	public void run() {
		for(int i=0;i<100;i++) {
			System.out.println("我是子线程"+i);
		}
	}

}

public class Demo {
	
	public static void main(String[] args) {
		
		DemoRunnable runnable =new DemoRunnable();
		Thread thread =new Thread(runnable);
		thread.start();
		
		
		for(int i=0;i<100;i++) {
			System.err.println("我是主线程"+i);
		}
	}
}
2.3 总结
Thread类也是实现了Runnable的,所谓的重写run方法,其实就是重写Runnable的方法
两种写法本质是一样的,最后 start() 调用的都是run方法

建议使用第二种写法:
1、解耦    各干各的事情,相互不纠缠    
    Runnable里面的run方法就干具体代码实现的事儿
    Thread 就干线程启动,关闭,休眠等线程的事儿
2、java是单继承的,一个类继承了 Thread就不可能继承其他的

?3. 多线程是如何运行的?

同一个CPU中,在同一个时刻,只可能有一个程序在运行
我们感知到的电脑可以干很多事情,或者我们的程序可以干很多事情,都是假象!
他们时刻交叉运行,其实就是看谁抢到了资源,谁就运行,存在随机性

####?4. 线程中的常见API

多线程中常见API:
//  1、设置线程优先级   1  5   10   
      thread.setPriority(Thread.MIN_PRIORITY);
     优先级高,是在多次运行后才能看到的效果,不要用这个东西替代逻辑
 // 2、线程的名字
      默认名字:每个线程都有默认的名字
         主线程:main
         子线程: Thread-i
      起名字:
        1、在构造方法中起名字
        2、通过setName起名字
        
		System.out.println(Thread.currentThread().getName());//main

		TalkThread th01 =new TalkThread("线程一");
		System.out.println(th01.getName());
		
		TalkThread th02 =new TalkThread();
		th02.setName("线程二");
		System.out.println(th02.getName());

 // 3、Thread.currentThread()  获取当前线程 这个对象
 // 4、Thread.sleep(毫秒数)   将当前线程进入休眠状态  毫秒值到了就会醒

?5. 线程安全?不安全?

#####5.1 火车站售票举例?

这里举个例子,火车站窗口售票的问题

线程安全问题:多个线程抢占同一资源,有可能引发线程安全问题
	多个线程--> 四个窗口
	同一资源--> num
现象:同一张票被卖出去多次

public class Demo {

	public static void main(String[] args) {
	
		/*Object obj =new Object();
		TicketTread t1 = new TicketTread("窗口一",obj);
		TicketTread t2 = new TicketTread("窗口二",obj);
		TicketTread t3 = new TicketTread("窗口三",obj);
		TicketTread t4 = new TicketTread("窗口四",obj);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();*/
		
		TicketTread2 t1 = new TicketTread2("窗口一");
		TicketTread2 t2 = new TicketTread2("窗口二");
		TicketTread2 t3 = new TicketTread2("窗口三");
		TicketTread2 t4 = new TicketTread2("窗口四");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	
	}

}

class TicketTread extends Thread{

	
	static int num = 100;//num属于类的,TicketTread ,该类对象只有一个
	
	//static Object obj =new Object();
	Object obj = null;
	
	public TicketTread(String name,Object obj) {
		
		super(name);//调用的是父类的有参数的构造方法,而且必须写在第一句
		this.obj = obj;
	}
	
	@Override
	public void run() {
		
		//同步代码块
		while(num > 0) {
			
			//synchronized (obj) {
			synchronized ("锁") {// “锁” 将来存放到常量池中
				if(num == 0) {
					System.out.println("票卖完了!!!");
					break;
				}
				System.out.println(getName() + "窗口卖出了第"+num+"张票");
				num--;
				
			}
		}	
	}
}
5.2 解决线程不安全的方法
解决线程安全问题:(12)
//1、加同步代码块锁
	synchronized(mutex) {
        
	}
'锁':什么东西可以充当锁呢?
任何对象都可以充当锁mutex,建议使用类的老父亲   Object obj =new Object();

'注意':obj可以放在类里,加static,这样?就唯一了
	 也可以在main方法中new一个,把obj作为参数,用this传参
	 或者用字符串常量"锁"等等,也能起到唯一的作用
	 
//2、同步方法,就是在方法上直接加synchronized
	public synchronized void run() {
	
	}
//谁是锁??
	如果在普通方法上加锁,锁对象是就是该类的实例化对象   this
	如果方法是静态方法,锁对象就是该类的Class对象   Class clazz

//两种方案建议使用第一种:
	1、灵活   想在哪个地方加都可以
	2、高效   一个方法上加了锁,这个方法中所有代码必须都执行完才能释放锁,效率太低了

//这里说明:
	Servlet 线程不安全:
	Servlet对象是由tomcat【web容器  tomcat\webLogic\Jboss...】创建的
	在一个web容器中只有一个serlvet对象
	
	@WebSerlvet("/hello")
	public class HelloServlet extends HttpServlet{
        int num = 100;   //num如果再加加或者减减等数字还会变动
    }
	//一定不要在servlet里定义全局变量,不安全

?6. 如何停止一个线程??

1、stop方法可以停止一个线程,但是该方法已经废弃,不再使用
2、建议使用定义变量的方法,如果需要停止该线程,修改变量值

class VNCRunnable implements Runnable{

	private String name;
	public boolean isFalg = true;	//这里定义了一个boolean类型的变量
	public VNCRunnable(String name) {
		this.name = name;
	}
	@Override
	public void run() {
		while(isFalg) {
			for(int i=0;i<10;i++) {
				System.out.println(name+":"+i);
			}
		}
	}

}

public class Demo2 {
	
	public static void main(String[] args) {
		
		VNCRunnable runnable =new VNCRunnable("VNC线程");
		Thread thread =new Thread(runnable);
		thread.start();
		
		
		for(int i=0;i<100;i++) {
			System.err.println("我是主线程"+i);
			if(i==80) {
				//thread.stop();//不建议这样用,暴力执法,不够温柔
				runnable.isFalg = false;//建议使用这种,好处是可以完整执行最后一遍代码
			}
		}
		
		
	}
}

?7. 线程的通信

7.1 什么是线程的通信?
线程的通信:
   两个线程,一个线程给另一个线程发送消息,感觉跟通信一样
   
   一个线程负责生产蛋糕,一个线程负责购买蛋糕,而且还要做到生产一个,购买一个,交替往复
   
   wait()  等待
      将当前线程放入到等待的线程池中
   notify  唤醒
      从当前的线程池中随机唤醒一个等待的线程
      
      cake.notifyAll()   去cake线程池中,唤醒所有等待的线程,而不是去dog,stu等线程池
   使用wait()notify() 方法,必须在同步代码块中调用
      同步代码块儿的锁必须和wait,notify调用的对象一致

7.2 实例——蛋糕制作、购买、制作循环?
7.2.1 蛋糕类?
public class Cake {

	public String name;
	public float  price;
	//此标识用于判断此时是生产还是购买
	public boolean isMake = true;
}

######7.2.2 制作、购买——线程?

class MakeCakeRunnable implements Runnable{

	private Cake cake ;
	private String name;
	public MakeCakeRunnable(String name,Cake cake ) {
		this.name = name;
		this.cake = cake;
	}
	
	
	// json
	@Override
	public void run() {
		
		while(true) {
			
			// wait方法只能在同步代码块或者方法中运行
			synchronized (cake) {
				
				if(cake.isMake) {
					cake.name = "小猪佩奇蛋糕";
					cake.price = 88;
					
					System.out.println(name+",生产成功了一个"+cake.name);
					cake.isMake = false;
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
				try {
					cake.wait();//进入等待状态,线程阻塞
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			
		}
		
	}
	
}

class BuyCakeRunnable implements Runnable{

	private String name;
	Cake cake = null;
	public BuyCakeRunnable(String name,Cake cake) {
		this.cake = cake;
		this.name = name;
	}
	@Override
	public void run() {
		
		while(true) {
			
			if(cake.isMake==false) {
				System.out.println(name+"吃掉了"+cake.name+",该蛋糕是"+cake.price);
				cake.name="";
				cake.price = 0;
				
				
				cake.isMake = true;
				synchronized (cake) {
					cake.notify();//唤醒阻塞的线程
				}
				
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			
		}
		
	}
	
}


public class DemoMain {

	public static void main(String[] args) {
		
		Cake cake =new Cake();
		
		MakeCakeRunnable make =new MakeCakeRunnable("锦鲤坊",cake);
		BuyCakeRunnable buy =new BuyCakeRunnable("小明",cake);
		new Thread(make).start();
		new Thread(buy).start();

	}

}

晨考内容

//1、什么是进程,什么是线程?
	每个正在运行的程序就是一个进程;
	线程包含在进程中,一个进程可以有多个线程

//2、创建多线程的两种方式?
	① 编写一个类继承Thread类,重写run方法(线程中的所有逻辑都要写在run方法中),然后用start启动子线程
	② 编写一个类实现Runnable接口,重写run方法,实例化该对象,并将该对象,存入Thread的构造方法中,thread.start() 启动子线程
	
Thread类也是实现了Runnable的,所谓的重写run方法,其实就是重写Runnable的方法
两种写法本质是一样的,最后 start() 调用的都是run方法

//3、Thread类有哪几种构造方法?
	
//4、Thread类常用方法有哪些?
	setPriority() 优先级设置
	getName() 获取名称
	setName() 设置名称
	currentThread() 获取当前线程
	Thread.sleep() 将当前线程进入休眠
//5、什么是线程不安全?如何解决,并指出锁对象指的是谁?
	多个线程抢占同一资源,就有可能引发线程安全问题。
	加锁
	对于普通方法,锁的对象是该类的实例化对象
	对于静态方法,锁的对象是该类的class类对象(唯一)
	
//6、如何停止一个线程?
	定义一个变量,通过改变这个变量的值终止一个循环,从而温柔地停止一个线程
	
//7、说说线程的等待唤醒机制,使用时需要注意哪些问题?
	wait(); 进入等待状态,线程阻塞
    notify(); 随机唤醒进入等待池的线程中的一个
    需要注意两个方法要放在同步代码块中,且锁为两方法前面的变量名
    
//8、什么是反射?哪些地方用到了反射?
	反射就是在不实例化对象的情况下,拿到一个类中所有内容的一种手段
	框架中大量用到反射
	
//9、获取一个类的Class对象的三种方式
	1. 直接通过类名
	2. 通过类的全路径获取.class
	3. 通过类的实例化对象获取
//10、Java中常用注解有哪些?注解有何作用?如何自定义注解?

你可能感兴趣的:(笔记)