java string和集合

来源:互联网 发布:时序数据挖掘 编辑:程序博客网 时间:2024/06/03 18:23
1. String str=new String("abc")和String str="abc"的字符串“abc”都是存放在堆中,而不是存在 
栈中。
 2. 其实在在java中有一个“字符数据池”的内存管理机制。
 3. String str="abc",执行这句话时,会先去“字符数据池”搜索时候有“abc”这个字符串,如果有


,则将字符串的首地址赋值给str,如果没有,生成一个新的字符串“abc”并且将首地址赋值给str;
 4. String str=new String("abc"),执行这句话时,不会考虑时候已经存在了“abc”这个字符串,而


是直接生成一个新的字符串“abc”并将首地址赋值给str,注意“abc”并不放在“字符数据池”中;
 5. 由以上分析可知,String str="abc"和效率要高于String str=new String("abc"),因为如果有重复


的字符串时,第一种方式可以节省空间。
 6. 下面举例说明一下,好好看看结果,仔细分析原因,上面已经说明的很清楚了:
public class Test{
 public static void main(String args[]){
  String s1=new String("abc");//直接在堆中生成新的“abc”
  String s2=new String("abc");//直接在堆中生成新的“abc”
  String s3="abc";//先去“字符数据池”搜索时候有“abc”这个字符串,如果有,则


将字符串的首地址赋值给s3,如果没有,则在“字符数据池”中生成一个新的字符串“abc”并且将首地


址赋值给s3;
  String s4="abc";//去“字符数据池”搜索时发现了上一步生成的“abc”这个字符串


,把该字符串首地址赋值给s4,这时其实s3和s4指向同一个字符数据池中的“abc”
  System.out.println(s1==s2);
  System.out.println(s1==s3);
  System.out.println(s2==s3);
  System.out.println(s3==s4);
 }
}


结果:
false
fasle
false
true


 


另外:例如:


String str1=”java”;    //指向字符串池
String str2=”blog”;   //指向字符串池




String s=str1+str2;   //s是指向堆中值为"javablog"的对象,+运算符会在堆中建立来两个String对象,这两个对象的值分别是"java" "blog". 也就是说从字符串池中复制这两个值,然后在堆中创建两个对象,然后再建立对象s,然后将"javablog"的堆地址赋给s.    这句共创建了?个String 对象!


System.out.println(s==”javablog”);   //结果是false。
Jvm确实对型如String str1=”java”;的String对象放在常量池里,但是它是在编译时那么做的,而String s=str1+str2;是在运行时刻才能知道,也就是说str1+str2是在堆里创建的,所以结果为false了。


如果改成一下两种方式:


String s="java" + "blog"; //直接将"javablog"放入字符串池中,System.out.println(s==”javablog”); 的结果为true, 这个句子创建了?个String对象


String s=str1+ "blog"; //不放入字符串池,而是在堆中分配,System.out.println(s==”javablog”); 的结果为False,    这个句子创建了?个String对象


总结


综上所述,创建字符串有两种方式:两种内存区域(pool,heap)


1," " 引号创建的字符串在字符串池中


2,new,new创建字符串时首先查看池中是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址;如果池中没有,则在堆中创建一份,然后返回堆中的地址(注意,此时不需要从堆中复制到池中,否则,将使得堆中的字符串永远是池中的子集,导致浪费池的空间)!


 


另外,对字符串进行赋值时,如果右操作数含有一个或一个以上的字符串引用时,则在堆中再建立一个字符串对象,返回引用;如String s=str1+ "blog";


 
-


四,String常用方法
1.compareTo方法: 从第一个开始,比到不一样的字符,就返回码值的差,后面的就不比了
如果没有比到不一样的,也就是包含关系,就返回长度的差值,如果长度也一样返回0
compareToIgnoreCase忽略大小写


2.concat连接字符串
3.contains: 判断是否包含指定字符串
4.endsWith:判断是否以指定字符串结尾
5.equals:比较两个字符串对象是否相等, equalsIgnoreCase 忽略大小写
6.getBytes() 编码,用本地字符集 getBytes(String charsetName)指定字符集
7.indexOf(int ch) 返回指定的字符在字符串中出现的首次出现的位置,如果没有返回-1
重载的方法indexOf(int ch, int fromIndex)指定偏移量
indexOf(String str)找子串indexOf(String str, int fromIndex)指定偏移量
8.lastIndexOf(String str) 从后往前找
9.length()返回字符串中字符的数量
10.split() 根据给定的字符串分割字符串
注意: a,b,c,  以逗号分割,只有三部分,后面的空字符串会被忽略
如果用字符串不包含的字符分割,会分割出一部分,就是字符串本身
11.trim去掉空格,只会去掉头尾的空格,中间的不去
12.substring(int beginIndex)  截取子串
13.toString() 将this String转为String
为什么定义此方法,在print("abc")时,会首先调用toString()方法,再输出
String类不重写同toString方法,就会自动复用父类的toString方法,此时就会输出字符串对象的地址


五,字符容器
StringBuffer和StringBuilder用法完全一样,唯一区别StringBuilder线程不安全,
而StringBuffer线程安全,一般都使用StringBuilder


六,object类的方法
protected  Object clone() 
          创建并返回此对象的一个副本。 
 boolean equals(Object obj) 内部源代码其实就是判断是否== ,我们可以重写此方法。
  ==比较的就是两个对象的引用地址是否相同。如果引用地址相同则相同。就相当比较了hashcode。
        
protected  void finalize() 
          当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 
 Class<?> getClass() 
          返回此 Object 的运行时类。 
 int hashCode() 返回的是对象的物理逻辑地址。我们可以重写此方法
          返回该对象的哈希码值。 
 void notify() 
          唤醒在此对象监视器上等待的单个线程。 
 void notifyAll() 
          唤醒在此对象监视器上等待的所有线程。 
 String toString() 
          返回该对象的字符串表示。 
 void wait() 
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 
 void wait(long timeout) 
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 
 void wait(long timeout, int nanos) 
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导 


两个obj,如果equals()相等,hashCode()一定相等。


两个obj,如果hashCode()相等,equals()不一定相等(Hash散列值有冲突的情况,虽然概率很低)。


所以:


可以考虑在集合中,判断两个对象是否相等的规则是:


第一步,如果hashCode()相等,则查看第二步,否则不相等;


第二步,查看equals()是否相等,如果相等,则两obj相等,否则还是不相等。 



一、为什么用集合
在程序中使用变量来持有对象,有些时候不知道需要持有多少个对象,
这种情况下用变量来持有不太现实,用存储对象的容器就可以解决这个问题


二、容器
所谓容器就是用于存放一系列数据的
StringBuffer和StringBuilder,只能存放字符
数组也是容器,长度不可变,数组只能存放相同类型的数据
集合,长度可变,可存储任意类型的对象
很明显,这里面最好用的容器就是集合


三、集合
    单列集合的父类Collection,Collectio下两个分支List和Set.  
       list集合(有序,允许有重复元素)
      ArrayList、Vector和LinkedList
这三个都是单列集合Collection下List集合的子类,所以他们中的元素都是有序的,允许有重复元素
ArrayList集合底层是数组实现,这样的实现注定对元素的操作查找快,增删慢
Vector和ArrayList一样,底层都是数组实现,区别在于,Vector线程安全而ArrayList线程不安全
LinkedLIst底层是链表结构实现,这样的实现对元素的操作增删快,查找慢
AbstractList的子类迭代有三种方式: 
1)使用get方法和size方法迭代
2)使用迭代器迭代,都可以使用iterator,而Vector有个特殊的迭代方式:通过elements方法获得Enumeration
3)使用增强for循环,如果使用了泛型的集合,变量可以声明为泛型,否则声明为Object。
增强for循环不能拿来修改容器中元素的值
泛型:为了操作容器的时候安全,需要给容器指定泛型,凡是出现类名的地方后面跟标签
集合增删元素注意的问题,
在迭代过程中,不能对改变列表(通过集合增删元素),需要调用迭代器的方法来增删元素
而Iterator只能删不能加,需要用ListIterator列表迭代器
         public class ArrayListTest {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Person("zhangsan", 19));
al.add(new Person("wangwu", 29));
al.add(new Person("lisi", 39));
al.add(new Person("zhangsan", 22));
al.add(new Person("zhangsan", 19));

//System.out.println(al.size());

//使用get和size遍历元素
System.out.println("使用get和size遍历元素");
for(int i=0; i<al.size(); i++) {
Object obj = al.get(i);
Person p = (Person) obj;
System.out.println(p);
}
System.out.println("-----------------");
//使用iterator迭代器迭代元素
System.out.println("使用iterator迭代器迭代元素");
Iterator iter = al.iterator();
while(iter.hasNext()) {
Person p = (Person) iter.next();
System.out.println(p);
}
System.out.println("--------------------");
//使用增强for循环遍历
System.out.println("使用增强for循环遍历");
for(Object obj : al) 
System.out.println(obj);
}


}
---------------------------------------------------------------------------------------      public class VectorTest {
-
public static void main(String[] args) {
Vector v = new Vector();
v.add(new Person("zhangsan", 19));
v.add(new Person("wangwu", 29));
v.add(new Person("lisi", 39));
v.add(new Person("zhangsan", 22));
v.add(new Person("zhangsan", 19));
// v.add("abc");

//使用Enumeration迭代
Enumeration e = v.elements();
while(e.hasMoreElements()) {
Person p = (Person) e.nextElement();
System.out.println(p);
}
}


}
-------------------------------------------------------------------------------------


          Set集合(无序,无重复元素)
1.HashSet:通过哈希算法保证无重复元素
在add方法中,首先调用对象的hashCode方法计算哈希值,折小作为位置号,如果该位置空着直接存进去
如果有元素了,需要和该位置元素一一进行equals比较,返回结果都为false才存进去,否则就不存结论:往hashSet中存的对象首先需要重写hashCode方法,注意一个原则:如果对象相等,hashCode必须相等,但是,尽量让不同对象的hashCode不相等其次要重写equals方法,因为最终去掉重复元素靠的就是equalsHashSet是很常用的,因为非常的高效


2.TreeSet集合:通过二叉树的原理保证无重复元素,并对元素进行排序
排序有两种方式:
1)按元素的自然顺序,向集合中存入的对象实现Comparable接口的compareTo方法
2)在创建TreeSet对象时,扔一个比较器进去,实现Comparator接口的compare方法
如果两种方式都使用的情况下,比较器具有较高的优先级
public class Person 
{
private String name;
private int age;

public Person(){}

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

public String getName() {
return name;
}

public int getAge() {
return age;
}

public String toString() {
return name + "@" + age;
}

@Override
public boolean equals(Object obj) {
if(this == obj)
return true;
if(obj instanceof Person) {
Person p = (Person) obj;
if(this.name.equals(p.name) && this.age==p.age)
return true;
}
return false;
}


@Override
public int hashCode() {
return name.hashCode()*31 + age;// 48 + 96 = 96 + 48
}




}




public class HashSetTest {


public static void main(String[] args) {
HashSet<Person> hs = new HashSet<Person>();

hs.add(new Person("zhangsan", 19));
hs.add(new Person("wangwu", 29));
hs.add(new Person("lisi", 39));


System.out.println(hs.size());
for(Person p : hs)
System.out.println(p);
}


}
--------------------------------------------------------------------------------------
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<Person> ts = new TreeSet<Person>(new MyComparator());
/*
TreeSet<Person> ts = 
new TreeSet<Person>(new Comparator<Person>(){


public int compare(Person p1, Person p2) {
int num = p1.getName().compareTo(p2.getName());
if(num!=0)
return num;
return p1.getAge() - p2.getAge();
}

});
*/
ts.add(new Person("zhangsan", 19));
ts.add(new Person("wangwu", 29));
ts.add(new Person("lisi", 39));
ts.add(new Person("wangwu", 19));
ts.add(new Person("zhangsan", 22));
ts.add(new Person("zhangsan", 19));

System.out.println(ts.size());

for(Person p : ts)
System.out.println(p);
}
}


class MyComparator implements Comparator<Person> {


public int compare(Person p1, Person p2) {
// 先按照年龄排序
int num = p1.getAge() - p2.getAge();
if(num!=0)
return num;
//程序走到这说明年龄相等
return p1.getName().compareTo(p2.getName());
}

}


//元素自然比较方法
public class Person implements Comparable 
{
public int compareTo(Object obj) {
// this obj
//先考虑按照姓名排序
Person p = (Person) obj;
if(this.name.compareTo(p.name)>0)
return 1;
if(this.name.compareTo(p.name)<0)
return -1;
if(this.age>p.age)
return 1;
if(this.age<p.age)
return -1;
return 0;

}


}
双列集合Map


1.HashMap
在HashMap存储对象的时候, 会先将key对象计算哈希值, 然后在集合中查找是否有哈希值相同的key对象.
如果没有哈希值相同的key对象, 直接将这个key和value存入集合.
如果有哈希值相同的key对象, 那么会将这个要存入的key对象和哈希值相同的key对象equals()比较.
如果比较结果不同, 将key和value存入.
如果比较结果相同, 则覆盖原有记录.
2.遍历Map集合
a).通过keySet()方法
使用Map的keySet()方法, 获取Map中所有key对象组成的一个Set集合.
遍历这个Set可以获得Map中的每一个key对象.
再使用Map的get()方法通过每一个key对象获取到每一个value对象.
b).通过entrySet()方法
使用Map的entrySet()方法, 获取Map中所有entry(记录,键值对)组成的一个Set集合.
遍历这个Set可以获得Map中的每一个entry(记录,键值对).
再使用Entry的getKey()和getValue()方法获取每一条记录的key和value.
3.TreeMap
在TreeMap存储键值对的时候, 会以二叉树形式存储, 每存一个键值对时, 会将key对象使用指定的比较算法进行比较, 确定其位置.
比较算法传入的方式有两种, 自然顺序, 比较器顺序, 如果都传, 优先比较器.
TreeMap和HashMap相比优势在于可以排序, 但效率略低.
4.Hashtable
原理和HashMap相同, 但线程安全, 效率略低. Hashtable不允许null键和null值.
5.Properties
Hashtable的子类, 一般用来操作.properties配置文件. key和value都用来存储Stirng类型
6.LinkedHashMap
HashMap的子类, 有存储顺序的HashMap. 

ublic class HashMapTest {


//HashMap保证键的唯一性和HashSet原理一样
public static void main(String[] args) {
HashMap<Person, String> hm = new HashMap<Person, String>();

hm.put(new Person("zhangsan", 19), "zhangsan");
hm.put(new Person("lisi", 23), "lisi");
hm.put(new Person("wangwu", 32), "wangwu");
hm.put(new Person("zhangsan", 29), "zhangsan");
hm.put(new Person("zhangsan", 19), "zhangsan");

System.out.println(hm.size());

//迭代Map集合
//1.先获得所有的键,再根据键取值
System.out.println("--------------------------------");
System.out.println("先获得所有的键,再根据键取值,不用泛型");
Set keys = hm.keySet();//获得所有键组成的集合
Iterator iter = keys.iterator();//获得迭代器
while(iter.hasNext()) {//循环迭代所有的key
Person key = (Person) iter.next();//获得一个key
String value = hm.get(key);//调用map的方法通过键获得值
System.out.println(key + "--" + value);//打印键值对
}
System.out.println("\r\n\r\n--------------------------------");
System.out.println("先获得所有的键,再根据键取值,使用泛型和增强for循环");
Set<Person> keyset = hm.keySet();
for(Person key : keyset)
System.out.println(key + "..." + hm.get(key));
System.out.println("\r\n\r\n--------------------------------");
//2.获得键值对映射关系Entry组成的set集合,再分别去键和值
System.out.println("获得键值对映射关系Entry组成的set集合,再分别去键和值");
Set entrys = hm.entrySet();//获得所有的键值对
iter = entrys.iterator();//产生迭代器
while(iter.hasNext()) {//循环迭代所有的键值对
Map.Entry e = (Entry) iter.next();//获得一个键值对
Person key = (Person) e.getKey();//获得键
String value = (String) e.getValue();//获得值
System.out.println(key + "--" + value);//打印键值对
}
System.out.println("\r\n\r\n--------------------------------");
System.out.println("迭代键值对,用泛型和增强for循环");
Set<Map.Entry<Person, String>> entryset = hm.entrySet();
for(Entry<Person, String> e : entryset) {
Person key = e.getKey();
String value = e.getValue();
System.out.println(key + "--" + value);

}
}


}




public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return name + "@" + age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Person))
return false;
final Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}


}


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


public class TreeMapTest {


/**
* @param args
*/
public static void main(String[] args) {
TreeMap<Person, String> tm =
new TreeMap<Person, String>(new Comparator<Person>(){
public int compare(Person p1, Person p2) {
int num = p1.getName().compareTo(p2.getName());
if(num!=0)
return num;
return p1.getAge()-p2.getAge();
}

});

tm.put(new Person("zhangsan", 19), "zhangsan");
tm.put(new Person("lisi", 23), "lisi");
tm.put(new Person("wangwu", 32), "wangwu");
tm.put(new Person("zhangsan", 29), "zhangsan");
tm.put(new Person("zhangsan", 19), "zhangsan");
/*
Set<Map.Entry<Person, String>> entrys = tm.entrySet();
for(Map.Entry<Person, String> me : entrys) {
Person key = me.getKey();
String value = me.getValue();
System.out.println(key + "---" + value);
}*/
Set entrys = tm.entrySet();
Iterator iter = entrys.iterator();
while(iter.hasNext()) {
Map.Entry me = (Entry) iter.next();
Person key = (Person) me.getKey();
String value = (String) me.getValue();
System.out.println(key + "---" + value);
}
}


}
-----------------------------------------------------------------------------------------




public class PropertiesTest {


public static void main(String[] args) throws FileNotFoundException, IOException {

//testProperties();


//readProperties();


//writeProperties();

updateProperties();
}


private static void updateProperties() throws IOException {
// 更新配置文件
//1 读配置文件
Properties prop = new Properties();

prop.load(new FileInputStream("src/a.properties"));

//2修改记录
prop.setProperty("name", "lyc");
prop.setProperty("age", "21");

//3写入配置文件
prop.list(new PrintStream("src/a.properties"));
}


private static void writeProperties() throws FileNotFoundException {
//写入配置文件
Properties prop = new Properties();
prop.setProperty("name", "xwh");
prop.setProperty("age", "32");

prop.list(new PrintStream("src/user.properties"));

}


private static void readProperties() throws IOException,
FileNotFoundException {
//读取配置文件,输出配置项
Properties prop = new Properties();
prop.load(new FileInputStream("src/a.properties"));
Enumeration e = prop.propertyNames();
while(e.hasMoreElements()) {
String name = (String) e.nextElement();
String value = prop.getProperty(name);
System.out.println(name + "=" + value);
}
}


private static void testProperties() {
//Properties的存取
Properties prop = new Properties();
prop.setProperty("name", "xwh");
prop.setProperty("age", "32");
prop.setProperty("name", "zhangsan");
System.out.println(prop.size());

String value = prop.getProperty("name");
System.out.println(value);

System.out.println("迭代Properties");
Enumeration<String> e = (Enumeration<String>) prop.propertyNames();
while(e.hasMoreElements()) {
String key = e.nextElement();
value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}


}










二.工具类
         四、工具类
1.可变参数
方法的形参可以定义为: int... num 意思是接受0到多个int值,这时num被作为了int数组处理
可变参数必须位于方法的最后一个参数,一个方法只能定义一个可变参数


2.Arrays
java.util包中提供的工具类,封装了对数组的常用操作


3.Collections
工具类,提供了关于集合的常用操作

五、包装类
1.java针对8中基本数据类型都提供了包装类,当基本数据类型被当做对象来用的时候会自动装箱成对象
2.以Integer为例
Integer x1 = 98;  Integer x2 = 98;  x1==x2? true, 因为有缓冲池,缓冲常量-128~127
Integer x = 1; x = x + 1; 经历了什么过程?
自动装箱 --> 自动拆箱 --> 自动装箱
新特性: 自动拆装箱技术
list.add(new Integer(1))  list.add(1);自动装箱
Integer x = list.get(0) int num = x.intValue();
int num = list.get(0);自动拆箱


六、System
常用方法 
System.exit(0)、 System.gc() 、System.getProperty()获得系统配置信息
System.currentTimeMillis(); 获得自1970年1月1日零时到现在的毫秒值(时间戳)
1.虚拟机一启动,系统就会为它分配内存,默认64M
修改内存最大值 java -Xmx200m
在eclipse下需要打开运行面板
java -D<name>=<value> 配置系统配置项

三.包装类
1.包装类名
除了int和char, 都是基本数据类型名首字母大写. int是Integer, char是Character



一.IO流概念
1.什么是IO流
在Java程序中用来处理设备之间的数据传输的工具.
程序读数据就用输入流, 写数据就用输出流.
2.流的分类
流按照处理数据的类型分为两种: 字节流, 字符流
流按照数据流向的类型分为两种: 输入流, 输出流
3.IO流的4个最大的抽象父类
Reader: 字符输入流
Writer: 字符输出流
InputStream: 字节输入流
OutputStream: 字节输出流
4.子类命名
FileInputStream 是 InputStream 的子类, 用来读取文件的字节输入流
FileReader 是 Reader 的子类, 用来读取文件的字符输入流
InputStreamReader 是 Reader 的子类, 用来从字节输入流中读取字符的流, 转换流
5.使用IO包中的类
使用前导包
使用时处理异常
使用后关闭流

二.字符流
1.逐个字符拷贝文件
使用FileReader的read()方法读取一个字符, 此方法读取到一个字符
使用FileWriter的write(int ch)方法写出一个字符
2.自定义数组拷贝文件
使用FileReader的read(char[] buf)方法读取一个数组
使用FileWriter的write(char[] buf, int off, int len)方法将数组中的数据写出, 指定从某个位置开始写, 写多少个
使用数组拷贝目的是为了尽量减少和文件打交道, 一次拷贝就多拷一些字符, 写出时一次也多写出一些.
3.使用带缓冲的输入输出流
使用BufferedReader的read()方法可以读取一个字符, 这个方法在第一次读取的时候会一次性读取8192个字符, 将其缓冲, 下次再读时从缓冲区中返回.
使用BufferedWriter的write(int ch)方法可以写出一个字符, 这个方法写出字符是先写出到缓冲区中, 直到缓冲区写满8192个之后, 才会真正写出.
4.逐行拷贝
使用BufferedReader的readLine()方法, 一次可以读取一行文本, 读的这行文本也是从缓冲区中获取的.
使用BufferedWriter的write(String s)方法, 一次写出一个字符串, 写出的字符串也是先写到缓冲区中.

三.包装设计模式
1.什么是包装设计模式
在使用一个类时, 发现这个类的功能不够强大, 对其进行包装, 在不改变原有功能的情况下, 增加新的功能.
2.包装设计模式的写法
定义一个成员变量, 持有一个被包装类对象的引用.
通过构造函数将一个被包装类的对象传入.
提供和被包装类相同的方法, 具有相同的功能.
在方法中调用被包装类的方法, 增加新功能, 使其更强大.
通常会继承被包装类或者和被包装类实现相同的接口, 这样我们的包装类就可以当做被包装类来使用了.

public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("src\\1.txt");
/*int ch;
while((ch=fr.read())!=-1) {
char c = (char) ch;
System.out.println(c);
}*/
char[] buf = new char[4];
int len;
while((len=fr.read(buf))!=-1) {
String data = new String(buf, 0, len);  
System.out.println(data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fr!=null)
fr.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}


}
---------------------------------------------------------------------------------------
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("src/2.txt", true);

fw.write("中国");
//fw.flush();
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(fw!=null)
fw.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}


}
---------------------------------------------------------------------------------------




public class BufferedCopy {
 * BufferedReader和BufferedWriter是包装流,提供了缓冲的功能,默认的缓冲区大小为8192
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader("src/a.txt"));
bw = new BufferedWriter(new FileWriter("src/b.txt"));
/*int ch;
while((ch=br.read())!=-1)
bw.write(ch);*/
//逐行拷贝
String line;
while((line=br.readLine())!=null) {
bw.write(line);
bw.newLine();
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
CloseUtil.close(br, bw);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


}
-----------------------------------------------------------------------------------


public class CloseUtil {
private CloseUtil(){}
public static void close(Reader r, Writer w) throws IOException {
try {
if(r!=null)
r.close();
} finally {
if(w!=null)
w.close();
}
}
}
-------------------------------------------------------------------------------------
public class LineNumberReaderTest {
     public static void main(String[] args) {
LineNumberReader lnr = null;
BufferedWriter bw = null;
try {
lnr = new LineNumberReader(new FileReader("src/a.txt"));
bw = new BufferedWriter(new FileWriter("src/c.txt"));
lnr.setLineNumber(1000);
String line;
while((line=lnr.readLine())!=null) {
int lineNumber = lnr.getLineNumber();
bw.write(lineNumber + " : " +line);
bw.newLine();
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
CloseUtil.close(lnr, bw);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


}
二.字节流
1.什么是字节流
InputStream和OutputStream的子类, 可以传输任意格式的二进制数据.
2.使用字节流拷贝文件
a).使用FileInputStream和FileOutputStream逐个字节拷贝
b).使用FileInputStream和FileOutputStream定义数组拷贝
c).使用BufferedInputStream和BufferedOutputStream内部的缓冲区拷贝
3.思考问题:
a).字节流是否可以拷贝文本文件: 
可以, 计算机中存储任何数据都是二进制, 字节流可以完成任何数据的传输
b).如果可以拷贝, 那么为什么还要用字符流, 什么时候用字符流
字符流提供了自动编码和解码的功能, 如果我们要读取字节显示成字符, 或者将字符输出, 这时用字符流就比较方便
在数据传输时要编码解码的话, 就需要使用字符流.
c).字节流读取字节时, 为什么返回int, 不直接返回byte
因为byte中能存储的所有的数据都有可能在文件中存在, 从-128到127都有可能在文件中存在. -1也可能在文件中存在.
如果直接返回一个普通的byte, 那么无法表示文件末尾.
Java中的read()方法, 是将读取到的byte高位补0转为正数的int返回. 这样做读取到的所有字节不论正负, 都会被转为正数.
这时遇到文件末尾就可以返回一个特殊的值, -1
在转为正数的时候, 其实是改变了原本的字节值的, 但是写出数据时, 我们会将这个int值强转, 在强转时会砍掉前面24位, 也就是补得24个0, 这时这个字节又恢复到原有值了.

三.转换流
1.什么是转换流
转换流是对字节流的包装, 使用字节流读取写出方法, 在其读取写出的功能上增强, 可以自动编码解码.
2.转换流使用
InputStreamReader可以将InputStream转换为Reader, 提供自动解码的功能, 在读取的时候将读取到的二进制数据转换为字符返回.
OutputStreamWriter可以将OutputStream转换为Writer, 提供自动编码的功能, 在写出数据的时候可以将要写出的字符转化为二进制写出.
3.应用场景
如果获取到一个字节流, 我们知道其中所有字节都对应字符, 那么可以将这个流转换为字符流, 操作更为方便.
在程序中获取到的一个字节流(不能自己创建字符流的情况), 想从中读取字符时就可以使用转换流将其转换.

四.标准输入输出流
1.System.in
标准输入流, InputStream的子类, 默认是从键盘输入读取. 
可以通过System.setIn()方法改变标准输入流
2.System.out
标准输出流, PrintStream, 打印流,Printstream是outputStream的子类。
可以通过System.setOut()方法改变标准输出流



五.流重点
1.清楚什么是字节流, 什么是字符流. 什么时候用字节流, 什么时候用字符流.
2.字节流的使用, 三种方式拷贝文件
FileInputStream和FileOutputStream, 逐个字节拷贝
FileInputStream和FileOutputStream, 自定义数组拷贝
BufferedInputStream和BufferedOutputStream, 使用包装类提供的缓冲功能拷贝
3.字符流的使用, 四种方式拷贝文件
FileReader和FileWriter, 逐个字符拷贝
FileReader和FileWriter, 自定义数组拷贝
BufferedReader和BufferedWriter, 使用包装类提供的缓冲功能拷贝, 逐个字符
BufferedReader和BufferedWriter, 使用包装类提供的缓冲功能拷贝, 逐行拷贝
4.会使用转换流, 将一个字节流转换为字符流. InputStreamReader, OutputStreamWriter
5.了解包装设计模式
6.清楚乱码的原理, 以及解决方式



0 0
原创粉丝点击