java Servlet 一、基础

创建工程时:

1、选择Dynamic Web Project

2、Dynamic Web Module version 选择2.5

一、Servlet基础

1.1 Servlet生命周期

1.1.1生命周期方法执行流程

实例化 —> 初始化 —> 服务 —> 销毁

Servlet的整个生命周期过程的执行,均由web服务器负责管理。即Servlet从创建到服务到销毁的整个过程中方法的调用,都是由web服务器调用执行,程序员无法控制其执行流程。

但程序员可以获取到Servlet的这些生命周期的时间点,并可以指定让Servlet做一些具体业务相关的事情。

实现Servlet接口,重写五个方法

public class SomServlet implements Servlet {

    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
    }

    public void destroy() {
        // TODO Auto-generated method stub
    }

    public ServletConfig getServletConfig() {
        // TODO Auto-generated method stub
        return null;
    }

    public String getServletInfo() {
        // TODO Auto-generated method stub
        return null; 
    }

    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
    }

}

对于以上代码的执行,注意以下几个时间点:

(1)、项目部署后启动服务器发现并没有执行Servlet的无参构造器,说明在Web容器启动时并没有创建Servlet对象

(2)、用户提交请求后,马上可以看到无参构造器、init()方法、service()方法均执行。

(3)、刷新页面,发现只会执行service()方法,每刷新一次,即每提交一次请求,就会执行一次service()方法。

(4)、让另外一个浏览器也发现同样的请求,会发现只执行service()方法,而无参构造器、init()方法均未执行。

(5)、正常关闭Tomcat(使用右键stop server关闭,不能使用Terminate关闭),发现destroy()方法也会执行。

1.1.2 Servlet特征

(1)、Servlet是单例多线程的

(2)、一个Servlet实例只会执行一次无参构造器方法与init()方法,并且是在第一次访问时执行。

(3)、每提交一次对当前Servlet的请求,就会执行一次service()方法

(4)、一个Servlet实例只会执行一次destroy()方法,在应用停止时执行。

(5)、由于Servlet是单例多线程的,为了保证其线程的安全性,一般情况下是不为Servlet类定义可修改的成员变量的。因为每个线程均可修改这个成员变量,会出现线程安全问题。

(6)、默认情况下,Servlet在Web容器启动时是不会被实例化的。

1.1.3 Web容器启动时创建Servlet实例

Servlet注册时,添加 int x ,x大于等于0,值越小,越优先被实例化。

<servlet>
  <servlet-name>SomServlet</servlet-name>
  <servlet-class>servlet01.SomServlet</servlet-class>
  <load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>SomServlet</servlet-name>
  <url-pattern>/SomServlet</url-pattern>
</servlet-mapping>

1.1.4 Servlet中的两个Map

服务器中存在有两个Map

Map-one

key —————— value

url-pattern ———– Servlet引用

提交请求,解析url,获取uri,查找map-one中的key,找到后,运行该Servlet的service()方法

Map-two

key —————- value

url-pattern ———

如果map-one中找不到对应的url-pattern,就会查找map-two,找到对应注册的servlet-class名,应用反射机制,创建该servlet实例,
把实例的引用放到map-one中,执行service()方法

1.2、ServletConfig

1.2.1 什么是ServletConfig

其实就是封装的注册中的servlet

<servlet>
   <description></description>
   <display-name>SomServlet</display-name>
   <servlet-name>SomServlet</servlet-name>
   <servlet-class>servlet01.SomServlet</servlet-class>
 </servlet>

1.2.2 获取ServletConfig

public class SomServlet implements Servlet {
    private ServletConfig config;
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        System.out.println(config);
    }

    public void destroy() {
    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public String getServletInfo() {
        return null; 
    }

    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
    }

}

tomcat服务器会通过init方法将ServletConfig对象传给servlet类

1.2.3 ServletConfig中的方法

public String getServletName();

public ServletContext getServletContext();

public String getInitParameter(String name);

public Enumeration getInitParameterNames();

web.xml
  <servlet>
  <description></description>
  <display-name>SomServlet</display-name>
  <servlet-name>SomServlet</servlet-name>
  <servlet-class>servlet01.SomServlet</servlet-class>
  <init-param>
          <param-name>adress</param-name>
          <param-value>beijing</param-value>
  </init-param>
  <init-param>
          <param-name>phone</param-name>
          <param-value>15022222222</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>SomServlet</servlet-name>
  <url-pattern>/SomServlet</url-pattern>
</servlet-mapping>
SomServlet.class
public class SomServlet implements Servlet {
    private ServletConfig config;
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
        System.out.println("servletconfig:"+config);
    }

    public void destroy() {
    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public String getServletInfo() {
        return null; 
    }

    public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        //获取servlet名称
        String servletName = config.getServletName();
        System.out.println("servletName:" + servletName);

        //获取servletcontext对象
        ServletContext  servletContext = config.getServletContext();
        System.out.println("ServletContext:" + servletContext);

        //获取该servlet里边的所有的初始化参数
        Enumeration<String> names = config.getInitParameterNames();
        while(names.hasMoreElements()) {
            String name = names.nextElement();
            String value = config.getInitParameter(name);
            System.out.println(name + "=" + value);
        }
    }

}
输出结果:
servletconfig:org.apache.catalina.core.StandardWrapperFacade@79bfdb2d
servletName:SomServlet
ServletContext:org.apache.catalina.core.ApplicationContextFacade@e11d81f
phone=15022222222
adress=beijing

1.3 ServletContext

代表整个应用,一个应用只有一个ServletContext

返回应用的名称

String getContextPath()

获取所有的servlet共享的初始化参数

Enumeration<String> getInitParameterNames();
String getInitParameter(String name)

获取真正的路径

String getRealPath(String path)

域属性空间:存放所有的servlet可以共享的属性

设置域属性,可以实现在一个servlet中设置,另一个servlet中获取

setAttribute(key,vlaue)
getAttribute(key)
removeAttribute(key)

1.4、设置欢迎页面

1.5、设置与匹配

1.5.1 设置

(1)、精确路径模式

<servlet-mapping>
    <servlet-name>some-servlet</servlet-name>
    <url-pattern>/some</url-pattern>
    <url-pattern>/test/other</url-pattern>
    <url-pattern>/aaa/bbb/ccc</url-pattern>
<servlet-mapping/>

(2)、通配符路径模式

<url-pattern>/some/*</url-pattern>

(3)、全路径模式

/*与/都是全路径模式,但他们还是不同的。

/*是真正的全路径模式,可以拦截所有请求,无论是动态资源还是表态资源请求,均会被拦截(service方法)。

/只会拦截静态资源请求    

(4)、后辍名模式

*.do

1.5.2 注意事项

后辍名模式的用法限制

带/的通配符路径模式与后辍名模式不能同时使用。例如:不能使用/*.do,/xxx/*.do

1.5.3 匹配原则

(1)、路径优先后辍

例如SomServlet的<url-patten>为*.do,OtherServlet的<url-pattern>为/xx/*,请求提交的url为:http://localhost:8080/oa/xxx/abc.do,同时匹配两个url-pattern时,会按照路径优先于后辍的匹配原则,选择OtherServlet。

(2)、精确路径优先

http://localhost:8080/oa/some    请求 同时匹配/some与/*时,优先配置/some

(3)、最长路径优先

http://localhost:8080/oa/some/other 请求 同时匹配/some/*和/some/other/*。优先选择/some/other对应的servlet

java-正则表达式

正则表达式用于操作字符串数据。

1、匹配:

匹配手机号码
String tel = "15022443344";
String regex = "1[3578][0-9]{9}";
String regex2 = "1[3578]\\d{9}";

boolean r = tel.matches(regex);
sosy(tel+":"+r);

1[3578][0-9]{9}
1[3578]\\d{9}

1:第一位固定是1 
[3578]:第二位是3578中的一个
[0-9]:第三位是0-9中的一个
{9}:第三位共出现9次
\:对\进行转义
\d:代表[0-9]

2、切割:

其实使用的就是String类中的split(String regex)方法

*:任意次数,0次,1次或多次
?:0次或1次
+:1次或多次

String str = "adf djs    sfjjs";
String names = str.split(" +");//空格出现一次或多次

===========================

.:代表任意字符
String str = "adf.djss.fjjs";
String names = str.split("\\.");

():代表组
//根据重复的t和重复的m切割三个字段
String str = "zhangsantttttttxiaoqiangmmmmmmmmzhaoliu";
String names = str.split("(.)\\1+");

(.)\\1+
.:代表第一个字符是任意字符,可以是t,也可以是m
(.):默认代表的是第1组
\\1:代表第二个字符是第1组的重复
+:代表重复的组出现1次或多次

组:((A)(B(C)))
最外边的()代表第1组
(A)是第2组
(B(C))是第3组
(C)是第4组

3、替换:

replaceAll(regex,replacement)

//用#代替所有的连续的重复字符
 String str = "zhangsantttttttxiaoqiangmmmmmmmmzhaoliu";
 str = str.replaceAll("(.)\\1+", "#");
 syso(str);
 输出结果:
 zhangsan#xiaoqiang#zhaoliu

========================

//将所有连续的重复字符变成单个,重复的t变成1个t,重复的m变成一个m
String str = "zhangsantttttttxiaoqiangmmmmmmmmzhaoliu";
 str = str.replaceAll("(.)\\1+", "$1");
syso(str);
 输出结果:
 zhangsantxiaoqiangmzhaoliu

$1:代表获取前一个参数中的组1

============================

String tel = "15022229999";
tel = tel.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2");
syso(tel);
输出结果:
150****9999

4、获取

//取三个字符组成的字符串
public class RegexGetDemo {

    public static void main(String[] args) {

        String str = "hao jlalal jie lskqjx wxm jajlw";
        String regex = "\\b[a-z]{3}\\b";
        //将正则封装成对象
        Pattern p = Pattern.compile(regex);
        //获取匹配器对象
        Matcher m = p.matcher(str);
        //查找
        while(m.find()) {
            System.out.println(m.group());
        }    
    }
}    
输出结果:
hao
jie
wxm

\b:表示边界

5、练习

String str = "我我...我我我....我我我要...要要要.....学学学....编编....程程程";
//去掉.
str = str.replaceAll("\\.+", "");
//去掉叠词
str = str.replaceAll("(.)\\1+","$1");
System.out.println(str);    

输出结果:
我要学编程

//ip地址排序
String ip_str = "192.168.9.30 127.0.0.1 4.4.4.5 101.80.8.44";

System.out.println("str:"+ip_str);

//补0,ip地址四段数字,不足3位的补足3位
ip_str = ip_str.replaceAll("(\\d+)", "00$1");

//四段数字全都保留32位
ip_str = ip_str.replaceAll("0*(\\d{3})", "$1");

//分割
String[] ips = ip_str.split(" +");

//treeSet排序
TreeSet<String> ts = new TreeSet<String>();
for(String ip:ips) {
    ts.add(ip);
}

//去掉补足3位的0
System.out.println("排序");
for(String ip:ts) {
    System.out.println(ip.replaceAll("0*(\\d+)","$1"));            
}

//校验邮箱地址
String mail = "test@126.com";//.cn .com.cn
String regex = "[a-zA-Z0-9_]+@[a-zA-Z0-9]+(\\.[a-zA-Z]{1,3})+";
boolean b = mail.matches(regex);
System.out.println(b);

网页爬虫,获取指定网页里所有的邮箱

public static List<String> getMails() throws IOException {
    //源
    URL url = new URL("http://www.baidu.com/web/aaa.html");
    //读取源
    BufferedReader bufIn = new BufferedReader(new InputStreamReader(url.openStream()));
    //对源中的数据进行规则匹配
    String mail_regex = "\\w+@\\w+(\\.\\w)+";
    List<String> list = new ArrayList<String>();
    Pattern p = Pattern.compile(mail_regex);
    String line = null;
    while((line = bufIn.readLine()) != null) {
        Matcher m = p.matcher(line);
        while(m.find()) {
            //数据写入集合
            list.add(m.group());
        }        
    }
    bufIn.close();
    return list;
}

java反射机制

1、概述

java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个类,都能够调用它的任意方法和属性,这种动态的获取信息以及动态调用对象的方法的功能就称为java语言的反射机制。

可以理解为对类的解剖。

2、获取Class类的对象的三种方式

(1)、Object类中的getClass()方法

Person p = new Person();
Class pc = p.getClass();

(2)、通过静态属性class

Class pc = Person.class;

(3)、通过Class类中的forName方法

String className = "src/reflect/Person";//带包的路径
Class pc = Class.forName(className);

3、获取Class中的构造函数

class Person {
    public String name;
    private int age;
    Person() {

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

//字节码文件对象,class对象的类
Class pc = Class.forName(className);

//生成该字节码文件对象,class对象,调用的是空参的构造函数
Object obj = pc.newInstance();

//实参的构造函数,生成对象,先获取构造函数
Constructor ct = pc.getConstructor(int.class,String.class);

//通过构造器对象的newInstance方法生成对象
Object obj = ct.newInstance(38,"小明");

4、获取Class中的字段

//字节码文件对象,class对象的类
Class pc = Class.forName(className);

//只能获取public字段
Field nameField = pc.getField("name");

//能获取public和private字段
Field ageField = pc.getDeclaredField("age");

//对私有字段的访问取消权限检查,暴力访问
ageField.setAccessible(true);

//生成对象
Object obj = pc.newInstance();

//age赋值
ageField.set(obj,22);

//age取值
Object ageValue = field.get(obj);

syso(ageValue);//22

5、获取Class中的方法

//字节码文件对象,class对象的类
Class pc = Class.forName(className);

//获取所有的public方法,包含父类中的
Method[] methods = pc.getMethods();

//获取本类中所有的public和private方法,不包含父类中的
methods = pc.getDeclaredMethods();

//获取指定的空参方法show,null代表空参
Method showMethod = pc.getMethod("show",null);

//生成对象
Object obj = pc.newInstance();

//调用show方法,null代表实参
showMethod.invoke(obj,null);

//获取指定的带参数的方法paramMethod
Method paramMethod = pc.getMethod("paramMethod", String.class, int.class);

//调用paramMethod方法
paramMethod.invoke(obj,"小强",22);

6、反射练习

public class ReflectPractice {
    public static void main(String[] args) throws IOException, ReflectiveOperationException {
        //主板开启
        Mainboard mb = new Mainboard();
        mb.run();

        //加载声卡,网卡等设备
        //读取配置文件,获取设备类名称
        File configFile = new File("pci.properties");
        if(!configFile.exists())
            configFile.createNewFile();
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream(configFile);
        prop.load(fis);
        for(int x=0; x<prop.size(); x++) {
            String pciName = prop.getProperty("pci"+(x+1));
            //根据设备名,获取该设备字节码文件对象
            Class clazz = Class.forName(pciName);
            //实例化设备对象
            PCI p = (PCI)clazz.newInstance();
            //运行设备
            mb.usePCI(p);
        }
        fis.close();
    }
}

public interface PCI {
    public void open();
    public void close();
}    

public class Mainboard {

    public void run() {
        System.out.println("mainboard run");
    }

    public void usePCI(PCI p) {
        if(p != null) {            
            p.open();
            p.close();
        }
    }

}

public class NetCard implements PCI {

    @Override
    public void open() {
        System.out.println("netcard open");
    }

    @Override
    public void close() {
        System.out.println("netcard close");
    }

}

public class SoundCard implements PCI{

    @Override
    public void open() {
        System.out.println("sound open");
    }

    @Override
    public void close() {
        System.out.println("sound close");
    }

}

file pci.properties

pci1=reflect.SoundCard
pci2=reflect.NetCard

只需要向配置文件中添加设备名称即可,不用再修改mainborad文件,增加了程序的扩展性。

IO流 - 十九:练习 - 按字节截取字符串

练习

public class CutStringByByte {

    public static void main(String[] args) throws UnsupportedEncodingException {
        /**
         * "ab你好"
         * 97 98 -60 -29 -70 -60
         * 
         * 在java中,字符串"abcd"与字符串"ab你好"的长度是一样的,都是四个字符。
         * 但是,对应的字节数不同,一个汉字占两个字节。
         * 定义一个方法:按照最大的字节数来截取子串。
         * 如:"ab你好",如果取三个字节,那么子串就是ab与你的半个字节,半个字节需要舍弃
         * 如果取四个字节,就是"ab你",取五个字节还是舍弃半个字节,还是"ab你"。
         */
        String str = "ab你好cd谢谢";
        int length = str.getBytes("gbk").length;
        for(int x=0; x<length; x++) {
            System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByByte(str, x+1));
        }

        System.out.println("=================================");

        /**
         * utf-8 一个汉字对应三个字节,而且都是负数
         * 
         */
        String str2 = "ab你好cd谢谢";
        int length2 = str2.getBytes("utf-8").length;
        for(int x=0; x<length2; x++) {
            System.out.println("截取"+(x+1)+"个字节结果是:"+cutStringByutf8(str2, x+1));
        }

    }

    private static String cutStringByutf8(String str, int length) throws UnsupportedEncodingException {
        byte[] buf = str.getBytes("utf-8");

        //从后向前计算负数(代表汉字的字节)的个数
        int count = 0;
        for(int x = length-1; x>=0; x--) {
            if(buf[x]<0)
                count++;
            else
                break;
        }

        if(count%3 == 0)
            return new String(buf,0,length,"utf-8");
        else if(count%3 == 1)
            return new String(buf,0,length-1,"utf-8");
        else
            return new String(buf,0,length-2,"utf-8");
    }

    public static String cutStringByByte(String str, int length) throws UnsupportedEncodingException {
        byte[] buf = str.getBytes("gbk");

        //从后向前计算负数(代表汉字的字节)的个数
        int count = 0;
        for(int x = length-1; x>=0; x--) {
            if(buf[x]<0)
                count++;
            else
                break;
        }
        //偶数个负数
        if(count%2 == 0)
            return new String(buf,0,length,"gbk");
        //奇数个负数
        else
            return new String(buf,0,length-1,"gbk");
    }

}

IO流 - 一、概述

(一)、字节流&字符流

输入流和输出流是相对于内存设备而言。

将外设中的数据读取到内存中:输入。

将内存的数据写入到外设中:输出。

字符流的由来:字节流读取文字字节数据后,不直接操作,而是先查指定的编码表,获取对应的文字,再对文字进行操作。

简单说,字符流就是字节流+编码表。

(二)、IO流常用基类

1、字节流的抽象基类:

InputStream, OutputStream

2、字符流的抽象基类:

Reader, Writer

(三)、字符流-FileWriter

换行和续写

public class IoDemo1 {

    private static final String LINE_SPERATOR = System.getProperty("line.separator");

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

        FileWriter fw = new FileWriter("demo.php",true);

        //临时存储到缓存区中
        fw.write("abc"+LINE_SPERATOR);

        //刷新,将数据写入文件
        fw.flush();

        //继续添加到缓存区
        fw.write("cde");

        //关闭流,关闭前会先调用flush刷新缓冲区中的数据到文件中。
        fw.close();

    }

}

异常处理

public class IoDemo2 {

private static final String LINE_SPERATOR = System.getProperty("line.separator");

public static void main(String[] args) {

    FileWriter fw = null;
    try {
        fw = new FileWriter("demo.php",true);
        fw.write("abc"+LINE_SPERATOR);
        fw.flush();        
        fw.write("cde");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if(fw != null)
                fw.close();
        } catch (IOException e) {
            throw new RuntimeException("关闭异常");
        }            
    }


}

(四)、字符流-FileReader

1、读取方式一 read()读取字符

public class IoDemo3 {

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

        FileReader fr = new FileReader("demo.txt");

        int ch = fr.read();

        while(ch > -1) {
            System.out.println((char)ch);
        }

    }

}

2、读取方式二

int read(char[])将数据存储到数组中,并返回数据的长度

public class IoDemo3 {

    public static void main(String[] args) throws IOException {
        /*
        FileReader fr = new FileReader("demo.txt");

        int ch = fr.read();

        while(ch > -1) {
            System.out.println((char)ch);
        }
        */
        FileReader fr = new FileReader("demo.txt");
        char[] buf = new char[1024];
        //将数据存储到buf数组中,并返回长度
        int length = fr.read(buf);
        while(length > -1) {
            System.out.println(new String(buf,0,length));
        }
        fr.close();

    }

}

(五)、练习

读取一个文件数据,并复写到另一个文件。

1、逐个字符读写

public class Demo4 {

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

        //字符读取流
        FileReader fr = new FileReader("demo.txt");

        //字符写入流
        FileWriter fw = new FileWriter("copy.txt");

        //读写
        int ch = 0;
        while((ch=fr.read()) > -1) {
            fw.write(ch);
        }

        //关闭流
        fr.close();
        fw.close();

    }

}

2、数组读写

public class Demo5 {

    private static final int BUFFER_SIZE = 1024;

    public static void main(String[] args) {
        FileReader fr = null;
        FileWriter fw= null;

        try {
            //读写流
            fr = new FileReader("demo.txt");
            fw = new FileWriter("copy.txt");

            //临时容器
            char[] buf = new char[BUFFER_SIZE];

            //定义变量记录读取到的字符,就是往数组里装的字符数
            int length = 0;
            while((length = fr.read(buf)) != -1) {
                fw.write(buf,0,length);
            }

        } catch (IOException e) {
            throw new RuntimeException("读写失败");
        } finally {
            if(fr != null)
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            if(fw != null)
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

IO流 - 四、转换流

字节流通向字符流的转换流:字符流Reader的子类InputStreamReader

public class Demo16 {
    public static void main(String[] args) throws IOException {
        //字节流
        InputStream in = System.in;

        //字节流转换成字符流,转换流
        InputStreamReader isr = new InputStreamReader(in);

        //高效字符流
        BufferedReader bufr = new BufferedReader(isr);

        String line = null;
        while((line = bufr.readLine()) != null) {
            if(line.equals("over"))
                break;
            System.out.println(line.toUpperCase());
        }
    }
}

字符通向字节流的转换流:字符流Writer的子类outputStreamWriter

public class Demo17 {

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

    BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

    BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

    String line = null;
    while((line=bufr.readLine()) != null) {
        if("over".equals(line))
            break;
        bufw.write(line.toUpperCase());
        bufw.newLine();
        bufw.flush();
    }
}

}

IO流 - 八、转换流的编码解码

1、OutputStreamWriter转换流。字符转换成字节:编码。

需求:将一个中文字符串按照指定的编码表,写入到一个文本文件中。其实就是需要将内存中的“你好”字符转成字节存到硬盘中。

分析:

明确使用指定编码表,就不可以使用FileWriter,因为FileWriter内部是使用默认的本地编码表。只能使用其父类OutputStreamWriter类。

OutputStreamWriter接收一个字节流对象,既然是操作文本,那么就应该是FileOutputStream。

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“a.txt”),charsetName);

需要高效

BufferedWriter bufw = new BufferedWriter(osw);

默认编码表:
FileWriter fw = new FileWriter("a.txt");
fw.write("你好");
fw.close;

指定编码表:
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),"UTF-8");
osw.write("你好");
osw.close;

FileWriter fw = new FileWriter(“a.txt”);

OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“a.txt”),”GBK”);

这两句代码的功能是等同的。

FileWriter:其实就是转换流并指定了本机默认编码表,而且是这个转换流的子类,用于方便的操作文本文件。

如果操作文件需要明确具体的编码。FileWriter就不可行了,必须用转换流。

2、InputStreamReader转换流。字节转换成字符:解码。

需求:读取硬盘中的文件内容,到控制台输出。

其实就是通过转换流,读取硬盘中的字节转换为字符到内存,再从内存通过System.out转换流,将字符转换为字节输出到控制台。

默认编码表:
FileReader fr = new FileReader("a.txt");
char[] buf = new char[10];
int length = fr.read(buf);
String str = new String(buf,0,length);
System.out.println(str);

指定编码表:
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),""utf-8"");
char[] buf = new char[10];
int length = isr.read(buf);
String str = new String(buf,0,length);
System.out.println(str);

IO流 - 五、需求演示

1、需求:将键盘录入的数据写入到一个文件中

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

        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo.txt")));

        String line = null;
        while((line=bufr.readLine()) != null) {
            if("over".equals(line))
                break;
            bufw.write(line.toUpperCase());
            bufw.newLine();
            bufw.flush();
        }
    }

2、需求:将一个文本文件内容显示在控制台上

BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("demo.txt")));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(System.out)));

String line = null;
while((line=bufr.readLine()) != null) {
    if("over".equals(line))
        break;
    bufw.write(line.toUpperCase());
    bufw.newLine();
    bufw.flush();
}

3、需求:将一个文本文件中的内容复制到另一个文件中

BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));

BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt")));

String line = null;
while((line=bufr.readLine()) != null) {
    if("over".equals(line))
        break;
    bufw.write(line.toUpperCase());
    bufw.newLine();
    bufw.flush();
}

IO流 - 十、IO流 - Properties集合

1、基本功能

Map

|—–Hashtable

|————-Properties

Properties集合特点:

1、该集合中的键和值都是字符串类型。

2、集合中的数据可以保存流中,或者从流中获取。

通常该集合用于操作以键值对形式存在的配置文件。

public class Demo1 {

    public static void main(String[] args) {

        Properties prop = new Properties();
        //存储
        prop.setProperty("zhangsan", "32");
        prop.setProperty("lisi", "32");
        prop.setProperty("wangwu", "32");
        prop.setProperty("zhaoliu", "32");
        //取出
        Set<String> names = prop.stringPropertyNames();

        for(String name:names) {
            String value = prop.getProperty(name);
            System.out.println(name+":"+value);
        }

    }

}

2、list方法

public void list(PrintStream out)

将属性列表输出到指定的输出流。此方法用于调试。

Properties prop = new Properties();
//存储
prop.setProperty("zhangsan", "32");
prop.setProperty("lisi", "32");
prop.setProperty("wangwu", "32");
prop.setProperty("zhaoliu", "32");
/*
//取出
Set<String> names = prop.stringPropertyNames();

for(String name:names) {
    String value = prop.getProperty(name);
    System.out.println(name+":"+value);
}
*/
//prop.list(System.out);

Properties props = System.getProperties();
props.list(System.out);    

3、store方法

store(OutputStream out,String comments)

Store(Writer writer,String comments)

将集合中的字符串,键值信息持久化存储到硬盘的文件中。

public class Dmo2 {

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

        Properties prop = new Properties();
        //存储
        prop.setProperty("zhangsan", "32");
        prop.setProperty("lisi", "32");
        prop.setProperty("wangwu", "32");
        prop.setProperty("zhaoliu", "32");

        //将这些集合中的键值对信息持久化存储到文件中,需要关联输出流。
        FileOutputStream fos = new FileOutputStream("info.txt");
        prop.store(fos, "name+age");

        fos.close();

    }

}

info.txt

#name+age
#Sat Jan 13 10:32:19 CST 2018
zhangsan=32
lisi=32
zhaoliu=32
wangwu=32

4、修改配置信息

load(InputStream inStream)

load(Reader reader)

读取配置信息

public class Demo3 {

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

        Properties prop = new Properties();

        FileInputStream fis = new FileInputStream("info.txt");

        prop.load(fis);
        prop.list(System.out);

    }
}

输出结果:
-- listing properties --
zhangsan=32
lisi=32
zhaoliu=32
wangwu=32

模拟load方法

public static void myLoad() throws IOException {
    Properties prop = new Properties();

    BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));

    String line = null;
    while((line = bufr.readLine()) != null) {
        if(line.startsWith("#")) 
            continue;
        String[] arr = line.split("=");
        System.out.println(arr[0]+"::"+arr[1]);
    }

}

修改硬盘的文件中已有的配置信息

public class Demo4 {

    /**
     * 读取文件
     * 键值对信息存储到集合中
     * 通过集合方法修改数据
     * 通过流将修改后的数据存储到文件
     * @param args
     * @throws IOException 
     */

    public static void main(String[] args) throws IOException {
        //读取文件流,流可以直接操作File对象
        File file = new File("info.txt");
        if(!file.exists()) {
            file.createNewFile();
        }
        FileReader fr = new FileReader(file);

        //创建空集合
        Properties prop = new Properties();

        //集合加载流数据
        prop.load(fr);
        //prop.list(System.out);

        //修改集合数据
        prop.setProperty("zhangsan", "20");

        //写入文件流
        FileWriter fw = new FileWriter(file);

        //集合持久化存储文件流
        prop.store(fw,"");

        //关闭流
        fr.close();
        fw.close();
    }

}

5、Properties集合 - 练习

定义功能,获取应用程序的运行次数,超过5次,给出到期提示并退出程序。

思路:使用计数器,并以文件形式保存在硬盘中。

public class Demo5 {
    public static void main(String[] args) throws IOException {
        //封装配置文件对象
        File confile = new File("count.properties");
        if(!confile.exists()) {
            confile.createNewFile();
        }

        //输入流
        FileInputStream fis = new FileInputStream(confile);

        //创建空集合
        Properties prop = new Properties();

        //加载数据流
        prop.load(fis);

        //获取数据
        String value = prop.getProperty("time");

        //计数器
        int count = 0;
        if(value != null) {
            count = Integer.parseInt(value);
            if(count >= 5) {
                throw new RuntimeException("使用次数到期");
            }
        }
        count++;

        //计数
        prop.setProperty("time", count+"");

        //输出流
        FileOutputStream fos = new FileOutputStream(confile);

        //存储
        prop.store(fos, "");

        fis.close();
        fos.close();

    }
}

6、Properties集合 - 练习 - 文件清单列表

获取指定目录下,指定扩展名的所有文件,把这些文件的绝对路径写入到一个文本中。

public class Demo6 {

    public static void main(String[] args) {
        //指定文件夹
        File dir = new File("test");

        //定义集合
        List<File> list = new ArrayList<File>();

        //定义过滤器
        FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith(".md");
            }
        };

        //深度遍历文件夹
        getFiles(dir,filter,list);

        //遍历list写入文件
        File destfile = new File("markdownlist.txt");
        writeToFile(list,destfile);


    }
    /**
     * 深度遍历,找到所有的文件并过滤,把符合条件的文件存储到容器中
     * @param dir
     * @param filter
     * @param list
     */
    public static void getFiles(File dir, FilenameFilter filter, List<File> list) {
        File[] files = dir.listFiles();
        for(File file:files) {
            if(file.isDirectory()) {
                //递归
                getFiles(dir, filter, list);
            } else {
                //过滤并添加集合
                if(filter.accept(dir, file.getName())) {
                    list.add(file);
                }
            }
        }
    }

    //写入到文件中
    public static void writeToFile(List<File> list, File destFile) {
        BufferedWriter bufw = null;
        try {
            bufw = new BufferedWriter(new FileWriter(destFile));
            for(File file:list) {
                bufw.write(file.getAbsolutePath());
                bufw.flush();
                bufw.newLine();
            }
        } catch (IOException e) {
            throw new RuntimeException("写入失败");
        } finally {
            if(bufw != null)
                try {
                    bufw.close();
                } catch (IOException e) {
                    throw new RuntimeException("关闭失败");
                }
        }

    }

}

IO流 - 九、File对象

1、File对象 - 构造函数&字段

//可以将一个已存在的,或者不存在的文件或者目录,封装为File对象
File f1 = new File("c:\\a.txt");
File f2 = new File("c:\\","a.txt");
File f3 = new File("c:\\");
File f4 = new File(f,"a.txt");
File f5 = new File("c:"+File.separator+"abc\\a.txt");
System.out.println(f5);//c:\abc\a.txt

2、File对象 - 常用方法

(1)、获取

获取文件名称、文件路径、文件大小、文件修改时间。
File file = new File("a.txt");
String name = file.getName();
String absPath = file.getAbsolutePath();
String path = file.getPath();
long length = file.length();
long time = file.lastModified();    

(2)、创建和删除

创建文件:如果文件不存在则创建,存在则不创建
File file = new File("a.txt");
boolean b = file.createNewFile();

删除文件:
boolean b = file.delete();    

创建文件夹目录:
File dir = new File("abc");
boolean b = dir.mkdir();//make directory

删除单个文件夹目录:
boolean b = dir.delete();

创建多级目录:
File dir = new File("a\\b\\c\\d");
dir.mkdirs();

(3)、判断

判断文件是否存在
File f = new File("a.txt");
boolean b = f.exists();

判断是否是文件,最好先判断是否存在,不存在则为false
boolean b = f.isFile();

判断是否是目录
boolean b = f.isDerectory();

(4)、重命名

//c盘的0.mp3重命名为了9.mp3并被剪切到了d盘
File f1 = new File("c:\\0.mp3");
File f2 = new File("d:\\9.mp3");
boolean b = f1.renameTo(f2);

(5)、系统根目录和容量获取

系统根目录
File[] files = File.listRoots();
for(File file:files) {
    System.out.println(file);
}
输出结果:/

File file  = new File("//");
System.out.println("getFreeSpace:"+file.getFreeSpace());
System.out.println("getTotalSpace:"+file.getTotalSpace());
System.out.println("getUsableSpace:"+file.getUsableSpace());

(6)、获取目录内容

String[] list()
获取当前目录下的文件以及文件夹的名称,包含隐藏文件。    调用list方法的File对象中封装的必须是目录。

File file = new File("//Users//haojie//Projects//hexo");
String[] contents = file.list();
for(String con:contents) {
    System.out.println(con);
}
输出结果:
scaffolds
.DS_Store
db.json
source
node_modules
public
.gitignore
package.json
_config.yml
.git
.deploy_git
themes             

(7)、过滤器

//通过文件名进行过滤
String[] list(FilenameFilter filter);

public class ListDemo {

    public static void main(String[] args) {
        File dir = new File("//Users//haojie//Projects//hexo//source//_posts");
        String[] names = dir.list(new FileFilterBySuffix(".md"));
        for(String name:names) {
            System.out.println(name);
        }
    }

}

public class FileFilterBySuffix implements FilenameFilter {

    private String suffix;


    public FileFilterBySuffix(String suffix) {
        super();
        this.suffix = suffix;
    }

    public boolean accept(File dir, String name) {
        //通过文件后缀名进行过滤
        return name.endsWith(suffix);
    }

}

//通过文件对象进行过滤
File[] listFiles(FileFilter filter)

public class ListFilesDemo {

    public static void main(String[] args) {
        File dir = new File("//Users//haojie//Projects//hexo");
        File[] files = dir.listFiles(new FileFilterByHidden());

        for(File file:files) {
            System.out.println(file);
        }
    }

}    

public class FileFilterByHidden implements FileFilter {

    @Override
    public boolean accept(File pathname) {
        //过滤非隐藏文件
        return !pathname.isHidden();
    }

}    

3、File对象 - 练习

(1)、深度遍历文件夹

//列出指定文件夹下的所有目录和文件并按层级缩进
public class Demo3 {

    public static void main(String[] args) {
        File dir = new File("//Users//haojie//Projects//hexo//source");
        listAll(dir,0);
    }

    private static void listAll(File dir,int level) {

        System.out.println(getSpace(level)+dir.getName());                
        level++;

        //获取指定目录下当前的所有文件夹或者文件对象
        File[] files = dir.listFiles();

        for (int i = 0; i < files.length; i++) {
            if(files[i].isDirectory()) {
                listAll(files[i],level);
            }else {
                System.out.println(getSpace(level)+files[i].getName());                
            }
        }
    }

    private static String getSpace(int level) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < level; i++) {
            sb.append("    ");
        }
        return sb.toString();
    }

}

(2)、递归

注意:递归一定要明确条件,否则会栈溢出

public class Demo4 {

    public static void main(String[] args) {
        toBinary(6);
        System.out.println(getSum(5));
    }

    //num!
    private static int getSum(int num) {
        if(num == 1)
            return 1;
        return num + getSum(num-1);
    }

    //转二进制
    private static void toBinary(int num) {

        if(num > 0) {
            toBinary(num/2);
            System.out.println(num%2);
        }

    }

}

(3)、删除目录

从里往外删,删除指定目录及目录下所有的目录和文件

public class Demo5 {

    public static void main(String[] args) {

        File dir = new File("//Users//haojie//a");
        removeDir(dir);

    }

    private static void removeDir(File dir) {
        File[] files = dir.listFiles();

        for(File file:files) {
            if(file.isDirectory()) {
                removeDir(file);
            } else {
                System.out.println(file+":"+file.delete());
            }
        }

        System.out.println(dir+":"+dir.delete());
    }

}