Java学习笔记

String

public class Test {

    public static void main(String[] args) {
        String str = "agbdfgadda";
        //1.是否以指定字符串开头
        boolean flag = str.startsWith("agbd");
        //true
        System.out.println(flag);

        //2.是否包含另一个字符串
        flag = str.contains("dda");
        //true
        System.out.println(flag);
        int index = str.indexOf("cf");
        if(index == -1)
            //not contains
            System.out.println("not contains");

        //3.字符串中出现另一个字符串的位置
        index = str.indexOf("bdf");
        //2
        System.out.println(index);

        //4.将指定位置的字符串替换为另一个字符串
        String after = str.replace("dda", "demo");
        //agbdfgademo
        System.out.println(after);

        //5.将字符串比较大小,String类实现了comparable接口,所以有了compareTo方法,可以比较
        String compareStr = "ab";
        int result = str.compareTo(compareStr);
        //负数小于,0等于,正数大于
        //5
        System.out.println(result);

        //6.将字符串转换为字符数组或者字节数组
        char[] chars = str.toCharArray();
        //agbdfgadda
        System.out.println(chars);
        byte[] bytes = str.getBytes();

        //7.转成大小写
        String upperString = str.toUpperCase();
        String lowerString = str.toLowerCase();
        //AGBDFGADDA
        System.out.println(upperString);

        //8.将字符串按照指定方式分解成多个字符串
        str = "lisi,wangwu,zhaoliu";
        String[] names = str.split(",");
        //lisi
        //wangwu
        //zhaoliu
        for(int i=0; i<names.length; i++)
            System.out.println(names[i]);

    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * 对字符串数组进行排序
         */
        String[] str = {"abc","nba","abb","zzz"};
        Arrays.sort(str);
        //abb abc nba zzz
        for(int i=0; i<str.length; i++)
            System.out.println(str[i]);
    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * 查找一个字符串中含有多少子串
         */
        String str = "adgvdfcfdgdfswcfdfkcdcf";
        String key = "cf";
        int index = 0;
        int count = 0;
        while((index = str.indexOf(key, index)) != -1) {
            index += key.length();
            count++;
        }
        //3
        System.out.println(count);
    }
}

Comparable(接口)

public class Person implements Comparable {

    private String name;
    private int age;

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    /*
     * 名字和年龄相同即认为相同
     */
    @Override
    public boolean equals(Object obj) {
        if(this == obj)
            return true;
        if(!(obj instanceof Person))
            throw new ClassCastException("类型错误");
        Person p = (Person)obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

    /*
     * 按年龄大小排序
     */
    @Override
    public int compareTo(Object o) {
        if(!(o instanceof Person))
            throw new ClassCastException("类型错误");
        Person p = (Person)o;
//      if(this.age > p.age)
//          return 1;
//      else if(this.age < p.age)
//          return -1;
//      return 0;
        return this.age - p.age;
    }

}

StringBuffer(容器)

public class Test {

    public static void main(String[] args) {

        /*
         * StringBuffer操作
         */
        StringBuffer buffer = new StringBuffer();
        buffer.append("dsf").append("woca");
        buffer.insert(1, true);
        //dtruesfwoca
        System.out.println(buffer);
        buffer.replace(0, 4, "bbb");
        //bbbesfwoca
        System.out.println(buffer);
        String str = "a" + "b" + "c";
        //底层为buffer.append("a").append("b").append("c").toString();
    }
}

StringBuffer和StringBuilder的区别

StringBuilder:非同步,单线程访问效率高
StringBuffer:同步的,多线程访问安全(适用多个线程对缓冲区进行append,delete,insert等操作)

System

不需要实例化,都是静态的属性和方法
out对应标准输出流(显示器)
in对应的是键盘

public class SystemDemo {

    //方便移植
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

    public static void main(String[] args) {

        /*
         * System类演示
         */
        //当前时间到1970年1月1日午夜之间的时间差,以毫秒为单位测量,将开始时间和结束时间相减,可以得出程序的运行时间
        long time = System.currentTimeMillis();
        //1487856430296
        System.out.println(time);

        //太多了,简单举几个例子
        //java.runtime.name::Java(TM) SE Runtime Environment
        //sun.boot.library.path::C:\Program Files\Java\jre1.8.0_101\bin
        //java.vm.version::25.101-b13
        Properties prop = System.getProperties();
        Set<String> keySet = prop.stringPropertyNames();
        for (String key : keySet) {
            String value = prop.getProperty(key);
            System.out.println(key + "::" + value);
        }

        //获取指定信息,比如操作系统
        String osname = System.getProperty("os.name");
        //Windows 7
        System.out.println(osname);
        //获取系统中的行分隔符,这样该程序在移植时,很方便,不同的系统,获取该系统上行分隔符
        //例如回车换行在windows上是/r/n在linux上是/n
        //hello
        //world
        System.out.println("hello" + LINE_SEPARATOR + "world");
    }

}

Math

public class MathDemo {

    public static void main(String[] args) {

        /*
         * Math方法都是静态的
         */
        //获取参数右边的整数
        double d1 = Math.ceil(12.54);
        //获取参数左边的整数
        double d2 = Math.floor(12.54);
        //四舍五入
        double d3 = Math.round(12.54);
        //13.0
        System.out.println(d1);
        //12.0
        System.out.println(d2);
        //13.0
        System.out.println(d3);
        //返回a的b次方
        //1000.0
        System.out.println(Math.pow(10, 3));
        //产生1到6的随机数,random方法产生的是[0.0,1.0)范围内的随机数
        int num1 = (int)(Math.random() * 6 + 1);
        double num2 = Math.ceil(Math.random() * 6);
        Random r = new Random();
        int num3 = r.nextInt(6) + 1;
    }

}

集合框架

List-有序的,带索引的,通过索引就可以精确的操作集合中元素,元素是可以重复的,List提供了增删改查动作,新出的子类都是以List结尾的,通常都是非同步的


Vector,可以增长的数组结构,同步的
ArrayList,是数组结构,长度是可变的,原理是创建新数组+复制数组。查询速度很快,增删较慢,不同步的
LinkedList,是链表结构,不同步的,增删速度很快,查询速度较慢,可用于实现堆栈,队列


Set -不包含重复元素的集合,不保证顺序。而且方法和Collection一致,Set集合取出元素的方式只有一种,迭代器


HashSet,哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法
TreeSet,可以对Set集合中的元素进行排序,使用的是二叉树结构,保证元素唯一性的方法:使用对象的比较方法是0,视为相同元素不存,元素的排序比较有两种方式:
1.元素自身具备自然排序,其实就是实现了Comparable接口重写了compareTo方法。如果元素自身不具备自然排序,或 者具备的自然排序不是所需要的,这时只能用第二种方式
2.比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式。需要定义一个类实现Comparator 接口,重写compare方法


到此为止,在往集合中存储对象时,通常对该对象都需要覆盖hashCode,equals,同时实现Comparable接口,建立对象的自然排序,通常还有一个方法也会复习toString()


Map -内部存储的都是键值对,必须要保证键的唯一性


Hashtable:哈希表,是同步的,不允许null最为键和值。被HashMap替代
Properties:属性集,键和值都是字符串,而且可以结合流进行键值的操作
HashMap:哈希表,不是同步的,允许null作为键和值
LinkedHashMap:基于链表+哈希表。可以保证map集合有序(存入和取出的顺序一致)
TreeMap:二叉树,不是同步的,可以对map集合中的键进行排序


这里写图片描述

Collection(集合框架接口)

public class Test {

    public static void main(String[] args) {

        /*
         * Collection一般方法演示
         */
        Collection coll = new ArrayList<>();
        //添加
        coll.add("item1");
        coll.add("item2");
        //[item1, item2]
        System.out.println(coll);
        //删除
        coll.remove("item2");
        //[item1]
        System.out.println(coll);
        //判断是否包含
        //true
        System.out.println(coll.contains("item1"));
        //清楚元素
        coll.clear();
        //[]
        System.out.println(coll);
    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * Collection带all方法演示
         */
        Collection c1 = new ArrayList();
        Collection c2 = new ArrayList();
        Collection c3 = new ArrayList();
        c1.add("1"); c1.add("2"); c1.add("3");
        c2.add("1"); c2.add("5"); c2.add("6");
        c3.add("1"); c3.add("2"); c3.add("1");
        c1.addAll(c2);
        //c1 = [1, 2, 3, 1, 5, 6]
        System.out.println("c1 = " + c1);
        //true
        System.out.println(c1.containsAll(c3));
        //删除c1中和c3相同的元素
        c1.removeAll(c3);
        //[3, 5, 6]
        System.out.println(c1);
        //保留c1中和c2相同的元素
        c1.retainAll(c2);
        //[5, 6]
        System.out.println(c1);
    }
}

Iterator(接口,迭代器,用来取出集合中的元素)

public class Test {

    public static void main(String[] args) {

        /*
         * Iterator方法演示
         */
        Collection coll = new ArrayList();
        //存储的是对象的引用
        coll.add("item1");
        coll.add("item2");
        //可以这么写,但是存储的还是对象(基本数据类型的包装类)
        //coll.add(3);
        //获取容器的迭代器对象,通过iterator方法
        Iterator it = coll.iterator();
        //item1
        //item2
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        //5
        //5
        //循环结束it2就直接消失了,而上面一种不会消失
        for(Iterator it2 = coll.iterator(); it2.hasNext();) {
            //存储时提升为Object类型
            Object obj = it2.next();
            String str = (String)obj;
            System.out.println(str.length());
        }
    }
}

List(接口)

public class Test {

    public static void main(String[] args) {

        /*
         * List方法演示,支持curd,增删改查
         */     
        //添加元素
        List list = new ArrayList();
        list.add(0);
        list.add(1);
        list.add(2);    
        //删除元素
        list.remove(0);
        //修改元素
        list.set(1, 7);
        //获取元素,一种是迭代一种是遍历+get
        //1
        //7
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

ListIterator

是一个接口,继承自Iterator,Iterator接口不能在遍历过程中增加和修改,而ListIterator可以,用来遍历和修改List接口
例如在迭代过程中如果有Item2则增加Item3

public class Test {

    public static void main(String[] args) {

        /*
         * ListIterator方法演示
         */     
        List list = new ArrayList();
        list.add("item1");
        list.add("item2");
        Iterator it = list.iterator();
        while(it.hasNext()) {
            Object obj = it.next();
            if("item2".equals(obj))
                list.add("item3");
        }
        System.out.println(list);
    }
}

这种写法会触发java.util.ConcurrentModificationException
应该用下面这种方法

public class Test {

    public static void main(String[] args) {

        /*
         * ListIterator方法演示
         */     
        List list = new ArrayList();
        list.add("item1");
        list.add("item2");
        ListIterator it = list.listIterator();
        while(it.hasNext()) {
            Object obj = it.next();
            if("item2".equals(obj))
                //也可以修改,即set方法
                it.add("item3");
        }
        //[item1, item2, item3]
        System.out.println(list);
    }
}

LinkedList

public class Test {

    public static void main(String[] args) {

        /*
         * LinkedList方法演示
         */     
        LinkedList link = new LinkedList();
        link.addFirst("item1");
        link.addFirst("item2");
        link.addFirst("item3");
        //获取但不删除第一个元素
        //item3
        System.out.println(link.getFirst());
        //返回删除的元素
        //item3
        System.out.println(link.removeFirst());
        //item2
        //item1
        while(!link.isEmpty()) {
            System.out.println(link.removeFirst());
        }
    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * LinkedList模拟实现队列
         */
        Queue queue = new Queue();
        queue.myAdd("item1");
        queue.myAdd("item2");
        queue.myAdd("item3");
        //item1
        //item2
        //item3
        while(!queue.isNull()) {
            System.out.println(queue.myGet());
        }
    }
}
class Queue {
    private LinkedList link;
    public Queue() {
        link = new LinkedList();
    }
    public void myAdd(Object obj) {
        link.addFirst(obj);
    }
    public Object myGet() {
        return link.removeLast();
    }
    public boolean isNull() {
        return link.isEmpty();
    }
}

Set

是一个接口

public class Test {

    public static void main(String[] args) {

        /*
         * Set可以用来去重,不一定有序
         */
        Set set = new HashSet();
        set.add("456");
        set.add("123");
        set.add("123");
        //123
        //456
        for(Iterator it = set.iterator(); it.hasNext(); ) {
            System.out.println(it.next());
        }
    }
}

HashSet

哈希表重复元素存不进去,先看hashcode()是否一致,再判断是否equals(),hascode相同并且equals2个元素相同,是HashMap的一个实例

public class Student implements Comparable{

    /*
     * Student对象类往HashSet存储时要重写hashcode和equals方法
     * 往TreeSet存储时要继承comparable接口,建立学生的自然排序(对象的默认排序方式)
     */
    private String name;
    private int age;
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + 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 (getClass() != obj.getClass())
            return false;
        Student other = (Student) 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;
    }
    @Override
    public int compareTo(Object o) {
        if(!(o instanceof Student)) 
            throw new ClassCastException();
        Student stu = (Student)o;
        int temp = this.age - stu.age;
        return temp == 0 ? this.name.compareTo(stu.name) : temp;
    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * HashSet存储和自定义对象要重写hascode和equals方法
         */
        Set set = new HashSet();
        set.add(new Student("stu1", 10));
        set.add(new Student("stu2", 20));
        set.add(new Student("stu2", 20));
        //Student [name=stu1, age=10]
        //Student [name=stu2, age=20]
        for(Iterator it = set.iterator(); it.hasNext(); ) {
            System.out.println(it.next());
        }
    }
}

TreeSet

是TreeMap的一个实例

public class Test {

    public static void main(String[] args) {

        /*
         * TreeSet用元素的自然顺序进行排序
         */
        Set set = new TreeSet();
        set.add(new Student("stu1", 10));
        set.add(new Student("stu3", 20));
        set.add(new Student("stu2", 20));
        set.add(new Student("stu2", 20));
        //Student [name=stu1, age=10]
        //Student [name=stu2, age=20]
        //Student [name=stu3, age=20]
        for(Iterator it = set.iterator(); it.hasNext(); ) {
            System.out.println(it.next());
        }
    }
}

Comparator

是一个借口,基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 上面往TreeSet里面存储时是先按年龄后按姓名排序,而如果想先按姓名后按年龄排序就可以这样写

public class ComparatorByName implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        Student s1 = (Student)o1;
        Student s2 = (Student)o2;
        int temp = s1.getName().compareTo(s2.getName());
        return temp == 0 ? s1.getAge() - s2.getAge() : temp;
    }

}
public class Test {

    public static void main(String[] args) {

        /*
         * TreeSet用comparator进行排序
         */
        Set set = new TreeSet(new ComparatorByName());
        set.add(new Student("stu2", 10));
        set.add(new Student("stu1", 10));
        set.add(new Student("stu1", 20));
        //Student [name=stu1, age=10]
        //Student [name=stu1, age=20]
        //Student [name=stu2, age=10]
        for(Iterator it = set.iterator(); it.hasNext(); ) {
            System.out.println(it.next());
        }
    }
}
public class ComparatorByLength implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
        int temp =  s1.length() - s2.length();
        return temp == 0 ? s1.compareTo(s2) : temp;
    }

}
public class Test {

    public static void main(String[] args) {

        /*
         * 对字符串进行长度排序,由短到长
         */
        Set set = new TreeSet(new ComparatorByLength());
        set.add("hadf");
        set.add("ddf");
        set.add("wca");
        set.add("dfdfgdf");
        //ddf
        //wca
        //hadf
        //dfdfgdf
        for (Iterator it = set.iterator(); it.hasNext();)
            System.out.println(it.next());
    }
}

LinkedHashSet

public class Test {

    public static void main(String[] args) {

        /*
         * 提高唯一性元素的查询效率还想有序,使用HashSet的子类LinkedHashSet
         */
        Set set = new LinkedHashSet();
        set.add("ddf");
        set.add("wca");
        set.add("dfdfgdf");
        set.add("aa");
        //ddf
        //wca
        //dfdfgdf
        //aa
        for (Object object : set) {
            System.out.println(object);
        }
    }
}

Map

是一个接口

public class Test {

    public static void main(String[] args) {

        /*
         * Map基本方法演示
         */
        Map<String,String> map = new HashMap<String,String>();
        map.put("星期一", "mon");
        //键相同,值覆盖,并返回旧值
        //mon
        System.out.println(map.put("星期一", "monday"));
        map.put("星期日", "sunday");
        //{星期日=sunday, 星期一=monday}
        System.out.println(map);
        //根据键删除键值对,并返回值
        //sunday
        System.out.println(map.remove("星期日"));
        //{星期一=monday}
        System.out.println(map);
    }
}
public class Test {

    public static void main(String[] args) {

        /*
         * keySet,entrySet和 values方法演示
         */
        Map<String,String> map = new HashMap<String,String>();
        map.put("星期一", "monday");
        map.put("星期二", "tuesday");
        Set<String> keySet = map.keySet();
        //星期二::tuesday
        //星期一::monday
        for (Iterator<String> it = keySet.iterator(); it.hasNext();) {
            String key = (String) it.next();
            String value = map.get(key);
            System.out.println(key + "::" + value);
        }
        //星期二::tuesday
        //星期一::monday
        for (String string : keySet) {
            System.out.println(string + "::" + map.get(string));
        }

        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> it = entrySet.iterator();
        //星期二::tuesday
        //星期一::monday
        while(it.hasNext()) {
            Map.Entry<String, String> me = it.next();
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "::" + value);
        }
        //星期二::tuesday
        //星期一::monday
        for(Map.Entry<String, String> me : map.entrySet()) {
            String key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "::" + value);
        }
        Collection<String> values = map.values();
        //value::tuesday
        //value::monday
        for (String value : values) {
            System.out.println("value" + "::" + value);
        }
    }
}

HashMap

根据hashCode和equals判断键值是否相等

public class Student implements Comparable<Student>{

    /*
     * 重写hashCode和equals是为了保证HashMapTest里面知道同姓名和同年龄是一个相同的键值
     * 实现comparable接口是为了放进TreeSet中的时候进行比较,也是判断键值是否相等的依据
     */
    String name;
    int age;
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + 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 (getClass() != obj.getClass())
            return false;
        Student other = (Student) 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;
    }
    @Override
    public int compareTo(Student o) {
        int temp = this.age - o.age;
        return temp == 0 ? this.name.compareTo(o.name) : temp;
    }

}
public class HashMapTest {

    public static void main(String[] args) {

        /*
         * HashMap存储自定义对象
         */
        Map<Student,String> map = new HashMap<Student,String>();
        map.put(new Student("张三",29), "上海");
        map.put(new Student("李四",30), "北京");
        map.put(new Student("李四",30), "南京");
        //Student [name=张三, age=29]......上海
        //Student [name=李四, age=30]......南京   新值替换旧值的结果
        for (Student key : map.keySet()) {
            String value = map.get(key);
            System.out.println(key.toString() + "......" + value);
        }
    }

}
HashMap和HashTable的区别
  1. HashTable的方法是同步的,HashMap的方法不同步
  2. HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)
  3. HashTable有一个contains()方法,功能和containsValue()一样
  4. HashTable使用Enumeration(),HashMap使用Iterator()
  5. hash数组的初始化大小和增长方式不同
  6. 哈希值的使用不同,HashTable直接使用对象的hashCode,而HashMap会重新计算hash值。

TreeMap

根据compareTo判断键值是否相等

public class TreeMapTest {

    public static void main(String[] args) {

        /*
         * TreeMap存储自定义对象
         */
        Map<Student,String> map = new TreeMap<Student,String>();
        map.put(new Student("aaa",30), "上海");
        map.put(new Student("ddd",35), "北京");
        map.put(new Student("ccc",32), "南京");
        map.put(new Student("ccc",32), "上海");
        map.put(new Student("xxx",27), "太原");
        //Student [name=xxx, age=27]::太原
        //Student [name=aaa, age=30]::上海
        //Student [name=ccc, age=32]::上海
        //Student [name=ddd, age=35]::北京
        for (Map.Entry<Student, String> me : map.entrySet()) {
            Student key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "::" + value);
        }
    }

}
public class CompatorByName implements Comparator<Student> {

    /*
     * 按照姓名排序,定义一个比较器
     */
    @Override
    public int compare(Student o1, Student o2) {
        int temp = o1.getName().compareTo(o2.getName());
        return temp == 0 ? o1.age - o2.age : temp;
    }

}
public class TreeMapTest {

    public static void main(String[] args) {

        /*
         * TreeMap存储自定义对象时使用自己定义的比较器
         */
        Map<Student,String> map = new TreeMap<Student,String>(new CompatorByName());
        map.put(new Student("aaa",30), "上海");
        map.put(new Student("ddd",35), "北京");
        map.put(new Student("ccc",32), "南京");
        map.put(new Student("ccc",32), "上海");
        map.put(new Student("xxx",27), "太原");
        //Student [name=aaa, age=30]::上海
        //Student [name=ccc, age=32]::上海
        //Student [name=ddd, age=35]::北京
        //Student [name=xxx, age=27]::太原
        for (Map.Entry<Student, String> me : map.entrySet()) {
            Student key = me.getKey();
            String value = me.getValue();
            System.out.println(key + "::" + value);
        }
    }

}

范型

1安全机制
2将运行期间的ClassCastException转移到编译时期变成编译失败
3泛型技术,是给编译器使用的技术
4避免了强转的麻烦

public class TreeSetTest {

    public static void main(String[] args) {

        /*
         * 泛型演示
         */
        Set<String> set = new TreeSet<String>();
        set.add("aaa");
        set.add("bbb");
        //aaa
        //bbb
        for (Iterator<String> it = set.iterator(); it.hasNext();) {
            String str = it.next();
            System.out.println(str);
        }
    }

}

定义一个工具类对对象进行操作,比如设置和获取,JDK1.5以前是这么干的

public class GenericDemo {

    public static void main(String[] args) {

        /*
         * 没有泛型的做法
         */
        Tool t = new Tool();
        t.setObj("haha");
        //会引起ClassCastException
        //t.setObj(6);
        String str = (String)t.getObj();
        //haha
        System.out.println(str);
    }
}

class Tool {
    private Object obj;

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }
}

泛型类的使用

public class GenericDemo {

    public static void main(String[] args) {

        /*
         * 泛型类的使用
         */
        Tool<String> t = new Tool<String>();
        t.setObj("aa");
        //aa
        System.out.println(t.getObj());

        Queue<String> que = new Queue<String>();
        que.myAdd("1");
        que.myAdd("2");
        que.myAdd("3");
        //1
        //2
        //3
        while (!que.isNull()) {
            System.out.println(que.myGet());
        }
    }

}
//将泛型定义在类上,泛型类
class Tool<Q> {
    private Q obj;

    public Q getObj() {
        return obj;
    }

    public void setObj(Q obj) {
        this.obj = obj;
    }
}
class Queue<E> {
    private LinkedList<E> list;
    Queue() {
        list = new LinkedList<E>();
    }
    public void myAdd(E obj) {
        list.addLast(obj);
    }
    public E myGet() {
        return list.removeFirst();
    }
    public boolean isNull() {
        return list.isEmpty();
    }
}

泛型接口的使用

public class GenericDemo {

    public static void main(String[] args) {

        /*
         * 泛型接口的使用
         */
        new InterImpl<String>().show("haha");
    }

}

interface Inter<E> {
    void show(E e);
}

//子类能明确类型,直接写类型,明确不了继续写泛型

/*class InterImpl implements Inter<String> {

    @Override
    public void show(String e) {

    }   
}*/
class InterImpl<T> implements Inter<T> {

    @Override
    public void show(T e) {
        //haha
        System.out.println(e);
    }

}

泛型通配符

public class Person {

    /*
     * Person类
     */
    private String name;
    private int age;

    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}
public class Student extends Person {

    /*
     * Student类
     */
    public Student(String name, int age) {
        super(name, age);
    }

    @Override
    public String toString() {
        return "Student [name=" + getName() + ", age=" + getAge() + "]";
    }

}
public class Worker extends Person {

    public Worker(String name, int age) {
        super(name, age);
    }

    @Override
    public String toString() {
        return "Worker name= " + getName() + ", age=" + getAge() + "]";
    }

}
public class GenericDemo {

    public static void main(String[] args) {

        /*
         * 泛型统配符的使用
         */
        Set<Student> set = new HashSet<Student>();
        set.add(new Student("zhansan1",21));
        set.add(new Student("zhansan2",22));
        set.add(new Student("zhansan3",23));
        print(set);
        print1(set);

        List<Student> list = new ArrayList<Student>();
        list.add(new Student("lisi1",21));
        list.add(new Student("lisi2",22));
        list.add(new Student("lisi3",23));
        print(list);

        List<String> list1 = new ArrayList<String>();
        list1.add("1");
        list1.add("2");
        list1.add("3");
        print1(list1);
    }

    //可以打印set和list里面的内容
    private static void print(Collection<Student> list) {
        for (Iterator<Student> it = list.iterator(); it.hasNext();) {
            Student student = (Student) it.next();
            System.out.println(student);
        }
    }

    /*
     * 可以打印set和list和list1里面的内容
     * 当使用泛型类或者接口时,传递的具体的类型不确定,可以通过通配符(?)来表示
     */
    private static void print1(Collection<?> list) {
        for (Iterator<?> it = list.iterator(); it.hasNext();) {
            //迭代出来的元素只能使用Object的方法
            System.out.println(it.next());
        }
    }

}

泛型限定

public class GenericDemo {

    public static void main(String[] args) {

        /*
         * 泛型统配符的使用
         */
        Set<Student> set = new HashSet<Student>();
        set.add(new Student("zhansan1",21));
        set.add(new Student("zhansan2",22));
        set.add(new Student("zhansan3",23));
        //zhansan3
        //zhansan1
        //zhansan2
        print(set);

        List<Worker> list = new ArrayList<Worker>();
        list.add(new Worker("lisi1",21));
        list.add(new Worker("lisi2",22));
        list.add(new Worker("lisi3",23));
        //lisi1
        //lisi2
        //lisi3
        print(list);

    }

    /*
     * 只打印学生和工人的类型,但不能写Person
     * ? extends E 接收E类型或者E的子类型,上限
     * ? super E   接收E类型或者E的父类型,下限
     */
    private static void print(Collection<? extends Person> list) {
        for (Iterator<? extends Person> it = list.iterator(); it.hasNext();) {
            Person p = it.next();
            System.err.println(p.getName());
        }
    }

}

内部类

当A类中的内容要被B类直接访问,而A类还需要创建B的对象,访问B的内容时,这时,可以将B类定义到A类的内部,这样访问更为便捷,将B称为内部类(内置类,嵌套类)

内部类可以直接访问外部类中的所有的成员,包含私有的,而外部类想要访问内部类中的成员,必须创建内部类的对象

public class Outer {

    /*
     * 内部类的简单介绍
     */
    private int num = 4;
    //private int num1 = 4;
    class Inner {
        void show() {
            System.out.println("num " + num);
        }
        //非静态内部类中不允许定义静态成员,仅允许在非静态内部类中定义静态常量static final
        //想要在内部类中定义静态成员,内部类也要被静态修饰
        //static void show1()
    }
    void method() {

        //Outer.Inner inner = new Outer.Inner();
        //把上面简写就是这个样子
        Inner inner = new Inner();
        inner.show();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer outer = new Outer();
        //num 4
        outer.method();
    }

}

内部类作为成员时可以用的修饰符

public:不多见,因为更多时候内部类已经被封装到外部类中,不直接对外提供,但不随外部类的加载而加载
static:
测试情况1:直接访问Outer中的Inner内部类的非静态成员

public class InnerClassDemo {

    public static void main(String[] args) {

        /*
         * 用public修饰内部类
         * 直接访问Outer中的Inner内部类的非静态成员
         * 创建内部类对象就行了,内部类作为成员,应该先有外部类对象,再有内部类对象
         */
        Outer.Inner in = new Outer().new Inner();
        //num 4
        in.show();
    }

}

测试情况2:对静态内部类中的非静态成员进行调用

package practice2;
public class Outer {

    //静态方法只能访问静态变量,所以num要变成static
    private static int num = 4;
    //内部类被静态修饰后,随着Outer的加载而加载,可以把一个静态内部类理解为就是一个外部类
    static class Inner {
        void show() {
            System.out.println("num " + num);
        }

        static void staticShow() {
            System.out.println("staticShow run");
        }
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer.Inner in = new Outer.Inner();
        //num 4
        in.show();
    }

}

测试情况3:对静态内部类中的静态成员进行调用

public class InnerClassDemo {

    public static void main(String[] args) {

        //既然静态内部类已随外部类加载,而且静态成员随着类的加载而加载,就不需要对象,直接用类名调用即可
        //staticShow run
        Outer.Inner.staticShow();
    }

}

内部类访问外部类的原因

内部类能直接访问外部类的成员,是因为内部类持有了外部类的引用,外部类.this

对于静态内部类不持有外部类.this,而是直接使用外部类名.

class Outer {

    int num = 3;
    class Inner {
        int num = 4;
        void show() {
            int num = 5;
            //num 5
            System.out.println("num " + num);
            //num 4
            System.out.println("num " + this.num);
            //num 3
            System.out.println("num " + Outer.this.num);
        }
    }
    void method() {
        new Inner().show();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

局部内部类特点

内部类可以定义在外部类的局部位置上
类名或者接口名称中有.说明是内部类,或者内部接口

class Outer {

    int num = 3;
    void method() {
        int x = 5;
        class Inner {
            void show() {
                //x 5
                System.out.println("x " + x);
                //num 3
                System.out.println("num " + num);
            }
        }
        //jdk1.8可以访问没有被final修饰的局部变量,jdk1.8可以
        new Inner().show();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

内部类的继承或者实现

内部类是可以继承或者实现外部其他的类或者接口

abstract class AbsDemo {
    abstract void show();
}
class Outer {

    int num = 3;
    class Inner extends AbsDemo{

        @Override
        void show() {
            //num 3
            System.out.println("num " + num);
        }
    }
    void method() {
        new Inner().show();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

内部类对象对外提供功能的访问方式

abstract class AbsDemo {
    abstract void show();
}
class Outer {

    int num = 3;
    public class Inner extends AbsDemo {

        @Override
        void show() {
            //num 3
            System.out.println("num " + num);
        }
    }
    public Inner getObjet() {
        return new Inner();
    }
    private class Inner2 extends AbsDemo {

        @Override
        void show() {
            //num 3
            System.out.println("num " + num);
        }
    }
    public Inner2 getObject2() {
        return new Inner2();
    }
}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        //如果Inner对外提供,可以如此获取
        Outer.Inner in = out.getObjet();
        in.show();
        //如果Inner被private,可以通过父类型获取
        AbsDemo abs = out.getObject2();
        abs.show();
    }

}

匿名内部类

匿名内部类其实就是一个带有内容的子类对象,匿名内部类是内部类的简化形式,匿名内部类有前提,内部类必须要继承父类或者实现接口

abstract class AbsDemo {
    abstract void show();
}
class Outer {

    int num = 3;
    void method() {
        //不想创建具体的子类型,还想创建AbsDemo的子类对象
        //可以直接使用父类型,抽象类不能new对象是因为抽象方法没有重写,直接重写就行
        new AbsDemo() {

            @Override
            void show() {
                //num 3
                System.out.println("num " + num);
            }
        }.show();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

如果匿名内部类有2个方法,可以写成如下形式

interface Inter {
    void show1();
    void show2();
}
class Outer {

    int num = 3;
    void method() {
        //内部类中方法不要过多,阅读性会很差
        Inter in = new Inter() {

            @Override
            public void show1() {
                //show1
                System.out.println("show1");
            }

            @Override
            public void show2() {
                //show2
                System.out.println("show2");
            }

        };
        in.show1();
        in.show2();
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

小练习

class Outer {

    int num = 3;
    void method() {

        //下面2中写法的区别
        new Object() {
            public void show() {}
        }.show();//可以编译通过

        /*Object obj = new Object() {
            public void show() {}
        };
        obj.show();*/
        //编译失败,匿名内部类是之类对象,当Object obj指向时,就被提升为Object,而Object类中没有
        //定义show()方法,编译失败
    }

}
public class InnerClassDemo {

    public static void main(String[] args) {

        Outer out = new Outer();
        out.method();
    }

}

多线程

创建线程的方法

1继承Thread类
1.1定义一个类继承Thread
1.2重写run方法
1.3创建子类对象,就是创建线程对象
1.4调用start方法

package practice2;

class Demo extends Thread {
    private String name;
    Demo(String name) {
        this.name = name;
    }
    public void run() {
        for (int i=0; i<10; i++)
            System.out.println(name + i);
    }
}

public class ThreadDemo {

    public static void main(String[] args) {

        /*
         * 继承Thread类实现多线程
         */
        Demo d1 = new Demo("张三");
        Demo d2 = new Demo("李四");
        //由主线程负责
        d1.run();
        //将d2这个线程开启
        d2.start();
    }

}

线程对象调用run方法和调用start方法的区别
调用run方法不开启线程,仅是对象调用方法

2.实现Runnable接口
2.1定义类实现Runnable接口
2.2覆盖接口中的run方法。将线程任务代码定义到run方法中
2.3创建Thread类的对象,只有创建Thread类的对象才可以创建线程
2.4将Runnable接口的子类对象作为参数传递给Thread类的构造函数,因为线程已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样线程对象创建时就可以明确要运行的线程的任务
2.5调用Thread类的start方法开启线程

class Demo implements Runnable {

    public Demo() {
        super();
    }

    @Override
    public void run() {
        for (int i=0; i<10; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }

}


public class ThreadDemo2 {

    public static void main(String[] args) {

        /*
         * 实现Runnable接口创建多线程
         */
        Demo d = new Demo();
        Thread d1 = new Thread(d);
        Thread d2 = new Thread(d);
        d1.start();
        d2.start();
    }

}

源码Thread类的一个样例

class Thread {
    private Runnable target;
    Thread (Runnable target) {
        this.target = target;
    }
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    public void start() {
        run();
    }
}

第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,即是线程对象,又有线程任务。
实现Runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型,Runnable接口对线程对象和线程任务进行解耦

synchronized和volatile的区别

  1. 关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰于变量,而synchronized可以修饰方法,以及代码块。随着JDK新版本的发布,synchronized关键字在执行效率上得到很大提升,在开发中使用synchronized关键字的比率还是比较大的
  2. 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞
  3. volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
  4. 关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。

线程安全包含原子性和可见性两个方面,Java的同步机制都是围绕这两个方面来确保线程安全的

IO

File

public class FileDemo {
    //提前封好一个,便于跨平台
    private static final String File_SEPARATOR = System.getProperty("file.separator");
    public static void main(String[] args) {

        /*
         * File类获取方法演示
         */
        File file = new File("e:" + File_SEPARATOR + "1.txt");
        //用File类封好的文件分隔符
        File file2 = new File("e:" + File.separator + "1.txt");

        //获取绝对路径
        String path = file.getAbsolutePath();
        String fileName = file.getName();
        long size = file.length();
        long time = file.lastModified();
        //e:\1.txt
        System.out.println(path);
        //1.txt
        System.out.println(fileName);
        //9
        System.out.println(size);
        //1488104499833
        System.out.println(time);

        String date = DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG).format(new Date(time));
        //2017年2月26日 下午06时21分39秒
        System.out.println(date);
    }
}
public class FileDemo {

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

        /*
         * 文件以及文件夹的创建,删除,以及存在
         */
        File file = new File("e:\\file.txt");
        /*
         * 创建文件,如果文件不存在,返回true
         * 存在,不创建,返回false
         * 路径错误,IOException
         */
        //文件创建,删除,判断
        boolean b1 = file.createNewFile();
        //ture
        System.out.println(b1);
        //删除完不去回收站
        boolean b2 = file.delete();
        //true
        System.out.println(b2);
        boolean b3 = file.exists();
        //false
        System.out.println(b3);
        //目录创建,删除,判断
        File dir = new File("e:\\haha\\haha\\haha");
        //true
        boolean b4 = dir.mkdirs();//创建多级目录,不加s是创建一个目录
        System.out.println(b4);
        //会把最有一个haha删掉,文件夹为e:\\haha\\haha
        //true
        boolean b5 = dir.delete();
        System.out.println(b5);
        //删除目录时,如果目录中有内容,无法直接删除,只有将目录中的内容都删除,保证为空时,才能删除
        //要判断是文件还是目录,必须先判断是否存在
        File file1 = new File("e:\\my.txt");
        //创建一个名字为my.txt的目录
        file1.mkdir();
    }
}
public class FileDemo {
    public static void main(String[] args) {

        /*
         * 获取目录下的文件信息
         */
        //健壮性判断,必须存在,必须是目录,否则容易引发数组为null,出现NullPointerException
        File dir = new File("e:\\images");
        String[] names = dir.list();
        //当前目录下文件和文件夹名字,
        for (String fileName : names) {
            System.out.println(fileName);
        }
        //当前目录下文件和文件夹对象
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file.lastModified());
        }
    }
}

文件名过滤器

public class FileMethodDemo {

    public static void main(String[] args) {

        /*
         * 获取目录中.txt文件
         */
        File dir = new File("D:\\haha");
        File[] files = dir.listFiles();
        //这样写太死了,程序扩展性差,要想获取其他文件,只能修改源代码
        //新建文本文档 (2).txt
        //新建文本文档 (4).txt
        for (File file : files) {
            if (file.getName().endsWith(".txt")) 
                System.out.println(file.getName());
        }
        //新建文本文档 (2).txt
        //新建文本文档 (4).txt
        File[] files1 = dir.listFiles(new FileNameFileterByTxt());
        for (File file : files1) {
            System.out.println(file.getName());
        }
        //新建文本文档 (2).txt
        //新建文本文档 (4).txt
        File[] files2 = dir.listFiles(new FileNameFilterBySuffix(".txt"));
        for (File file : files1) {
            System.out.println(file.getName());
        }
    }

}
public class FileNameFileterByTxt implements FilenameFilter{

    /*
     * 文件名过滤器
     */
    @Override
    public boolean accept(File dir, String name) {

        return name.endsWith(".txt");
    }

}
public class FileNameFilterBySuffix implements FilenameFilter{

    /*
     * 文件名过滤器
     */
    private String suffix;

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

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(suffix);
    }

}

文件过滤器

public class FileMethodDemo {

    public static void main(String[] args) {

        /*
         * 获取目录中文件夹
         */
        File dir = new File("D:\\haha");
        File[] files = dir.listFiles(new FileFilterByDir());
        //D:\haha\新建文件夹
        //D:\haha\新建文件夹 - 副本
        for (File file : files) {
            System.out.println(file);
        }
    }

}
public class FileFilterByDir implements FileFilter {

    /*
     * 文件过滤器
     */
    @Override
    public boolean accept(File pathname) {

        return pathname.isDirectory();
    }

}

字节输出流

OutputStream:输出字节流的超类,1操作的数据都是字节,2定义了输出字节流的基本共性功能,3输出流中定义的都是write方法,操作字节数组,操作单个字节,子类有规律,所有的子类名称后缀是父类名,前缀名是这个流对象功能

public class OutputStreamDemo {

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

        /*
         * 字节输出流,将数据写入文件
         */
        File dir = new File("tempfile");
        if (!dir.exists())
            dir.mkdir();
        File file = new File(dir,"file.txt");
        //文件如果没有,会抛 FileNotFoundException
        //输出流目的是文件,会自动创建,如果文件存在则覆盖
        FileOutputStream fos = new FileOutputStream(file);
        byte[] date = "this is for test".getBytes();
        fos.write(date);
        //关闭流资源
        fos.close();
    }

}
public class OutputStreamDemo {

    //window下/r/n是回车换行,linux下是/n
    private static final String LINE_SEPARATOR = System.getProperty("line.separator");

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

        /*
         * 将数据续写到文件中,并且可以换行
         */
        File file = new File("tempfile\\file.txt");
        //后面加参数true表示续写
        FileOutputStream fos = new FileOutputStream(file, true);
        String str = LINE_SEPARATOR + "test";
        fos.write(str.getBytes());
        fos.close();
    }

}
public class OutputStreamDemo {

    public static void main(String[] args) {

        /*
         * IO异常的处理
         */
        File file = new File("K:\\test.txt");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write("hahha".getBytes());
        } catch (IOException e) {
            System.out.println(e.toString());
        } finally {
            //如果fos创建失败为null,执行这一句会发生空指针异常
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    throw new RuntimeException("");
                }
            }
        }
    }

}

字节输入流

InputStream字节输入流的超类
int read() 读取一个字节并返回,没有字节返回-1
int read(byte[]) 读取一定量的字节数,并存储到字节数组中,返回读取到的字节数
available可以返回文件的大小

public class FileInputStreamDemo {

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

        /*
         * FileInputStream读取文件演示
         */
        File file = new File("tempfile//file.txt");
        FileInputStream fis = new FileInputStream(file);
        int ch = 0;
        //如果文件有回车,读取出来的也会回车
        //abcde
        while ((ch = fis.read()) != -1) {
            System.out.print((char)ch);
        }
        System.out.println();
        fis.close();
        FileInputStream fis1 = new FileInputStream(file);
        int len = 0;
        byte[] buf = new byte[2];
        //abcde
        while ((len = fis1.read(buf)) != -1) {
            System.out.print(new String(buf,0,len));
        }
    }

}
public class FileInputStreamDemo {

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

        /*
         * 复制文件
         */
        File srcFile = new File("tempfile//file.txt");
        File targetFile = new File("tempfile//copy_file.txt");
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(targetFile);
        int ch = 0;
        while ((ch = fis.read()) != -1) {
            fos.write(ch);
        }
        fis.close();
        fos.close();
    }

}
public class FileInputStreamDemo {

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

        /*
         * 复制文件
         */
        File srcFile = new File("tempfile//file.txt");
        File targetFile = new File("tempfile//copy_file_2.txt");
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(targetFile);
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        fis.close();
        fos.close();
    }

}

字符流

Reader读取字符流的抽象超类
read()读取单个字符并返回,
read(char[])将数据读取到数组中,并返回读取个数

public class CharStreamDemo {

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

        /*
         * 字节流读取字符的问题和统计一篇文章中有多少个好字
         */
        writeCNText();
        readCNText();
        readCNTextByReader();
    }

    private static void readCNTextByReader() throws IOException {
        FileReader fr = new FileReader("tempfile\\cn.txt");//这个流底层用的是FileInputStream
        int ch = 0;
        int count = 0;
        while ((ch = fr.read()) != -1) {
            if (ch == '好')
                count++;
        }
        //2
        System.out.println(count);
    }

    private static void readCNText() throws IOException {
        FileInputStream fis = new FileInputStream("tempfile\\cn.txt");
        //把这个大小定义为4,会读出a你和好的1个字节,因为中文一个字是2个字节
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len = fis.read(buf)) != -1) {
            String str = new String(buf,0,len);
            //a你好你好
            System.out.println(str);
        }
    }

    private static void writeCNText() throws IOException {

        FileOutputStream fos = new FileOutputStream("tempfile\\cn.txt");
        fos.write("a你好你好".getBytes());
        fos.close();
    }

}


Writer写入字符流的抽象超类
flush()和close()的区别
flush()将流中缓冲区缓冲的数据刷新到目的地中,刷新后,流还可以继续使用
close()关闭资源,但在关闭前会将缓冲区的数据刷新到目的地中,否则丢失数据,然后再关闭流,流不可以使用
写入数据多一定要一边写一边刷新,最后一次可以由close刷新

public class CharStreamDemo {

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

        /*
         * FileWriter方法演示
         */
        FileWriter fw = new FileWriter("tempfile\\fw.txt");
        fw.write("你好谢谢再见");//这些文字都要先编码,然后写入到流的缓冲区中
        fw.flush();
        fw.write("又回来了");
        fw.close();
    }

}

字符转换流

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

public class CharStreamDemo {

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

        /*
         * 能识别中文的码表有2个,GBK,UTF-8(3个字节存储一个汉字)
         * 能否将数据按照UTF-8的格式进行存储了?
         * 不用使用FileWriter了,因为FileWriter默认的是GBK,和操作系统有关
         */
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("tempfile\\u8cn.txt"), "utf-8");
        osw.write("你好");
        osw.close();
        InputStreamReader isr = new InputStreamReader(new FileInputStream("tempfile\\u8cn.txt"), "utf-8");
        char[] buf = new char[1024];
        int len = isr.read(buf);
        //你好
        System.out.println(new String(buf,0,len));
        isr.close();
    }

}

OutputStreamWriter的子类是FileWriter,InputStreamReader的子类是FileReader
OutputStreamWriter和InputStreamReader是字符和字节的桥梁,也可以称之为字符转换流
字符转换流原理:字节流+编码表
FileWriter和FileReader作为子类,仅作为操作字符文件的便捷类存在
当操作的字符文件使用的是默认的编码表时可以不用父类,而直接用子类就完成操作了,简化了代码
下面这三句代码一样
用子类的条件1:操作的是文件2:使用默认编码

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "GBK");
FileReader fr = new FileReader("a.txt");
public class CharStreamDemo {

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

        /*
         * 用自定义缓冲区复制文件
         */
        FileReader fr = new FileReader("tempfile\\file.txt");
        FileWriter fw = new FileWriter("tempfile\\copyfile.txt");
        char[] buf = new char[1024];
        int len = 0;
        while ((len = fr.read(buf)) != -1) {
            fw.write(buf, 0, len);
        }
        fr.close();
        fw.close();
    }

}
public class CharStreamDemo {

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

        /*
         * 用BufferReader和BufferWriter复制文件
         */
        BufferedReader bufr = new BufferedReader(new FileReader("tempfile//file.txt"));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("tempfile//copyfile.txt"));
        String line = null;
        while ((line = bufr.readLine()) != null) {
            bufw.write(line);
            bufw.newLine();
            bufw.flush();
        }
        bufw.close();
        bufr.close();
    }

}

其他流

IO练习

对象序列化

序列化接口的作用:没有方法,不需要覆盖,是一个标记接口为了启动序列化功能
唯一作用,给每一个需要序列化的类都分配一个序列版本号
这个版本号与该类相关联。这个版本号的作用是,在序列化时,会将这个序列号也保存到文件中,在反序列化时会读取这个序列化和本类的序列化进行匹配,如果不匹配会抛出java.io.InvalidClassException异常,是用于验证的

//标记接口,用于启动类的序列化功能
public class Person implements Serializable {
    //最好自己显示声明一个serialVersionUID
    private static final long serialVersionUID = 12345L;
    private String name;
    private int age;
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}
public class ObjectStreamDemo {

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

        /*
         * 对象的序列化和反序列化
         */
        writeObject();
        readObject();

    }

    //必须有这个类才能读出数据
    private static void readObject() throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream("tempfile\\obj.object");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Object obj = ois.readObject();
        //Person [name=zhang, age=30]
        System.out.println(obj.toString());
    }

    private static void writeObject() throws IOException {
        Person p = new Person("zhang",30);
        FileOutputStream fos = new FileOutputStream("tempfile\\obj.object");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(p);
    }

}

静态数据不会被序列化,name写成这样就不会被存进去,因为没必要类里面就有

private static String name;

对于一个非静态数据也不想序列化,需要一个关键字修饰

private transient /*瞬态*/ int age;

网络编程

网络通讯要素:IP地址,端口号,传输协议

public class IPDemo {

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

        /*
         * Ip对象
         */
        //获取本地主机地址对象
        InetAddress ip = InetAddress.getLocalHost();
        //192.168.0.103::LI-PC
        System.out.println(ip.getHostAddress() + "::" + ip.getHostName());
        //获取其他主机的地址对象,这里写成本机的了 
        InetAddress ip1 = InetAddress.getByName("192.168.0.103");
        //192.168.0.103::LI-PC
        System.out.println(ip.getHostAddress() + "::" + ip.getHostName());
    }

}

先运行接收端,再运行发送端

public class UDPSend {

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

        /*
         * 通过udp协议发送一段文本数据
         * 1.需要先建立udp的socket,他具备发送或者接收功能
         * 2.将数据封装到数据包中,数据包对象是DatagramPacket
         * 3.使用socket对象的send方法将数据包发送出去
         * 4.关闭资源
         */
        System.out.println("udp发送端");
        DatagramSocket ds = new DatagramSocket();
        String text = "hello udp is coming";
        byte[] buf = text.getBytes();
        DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.103"),10000);
        ds.send(dp);
        ds.close();
    }

}
public class UDPRece {

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

        /*
         * 定义一个UDP的接收端。接收发送过来的数据。并显示在屏幕上
         * 1.先有udpsocket服务,并且接收端一定要明确端口,否则收不到数据
         * 2.接收数据,先将数据存储到数据包中
         * 3.先定义数据包
         * 4.通过数据包对象获取数据包的内容,发送端的ip,发送端的端口,发送过来的数据
         * 5.关闭资源
         */
        System.out.println("udp接收端");
        DatagramSocket ds = new DatagramSocket(10000);
        byte[] buf = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        ds.receive(dp);//阻塞
        String ip = dp.getAddress().getHostAddress();
        int port = dp.getPort();
        String text = new String(dp.getData(),0,dp.getLength());
        //192.168.0.103 54035 hello udp is coming
        //54035是因为发送端没有明确端口
        System.out.println(ip + " " + port + " " + text);
        ds.close();
    }

}
public class UDPRece2 {

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

        /*
         * 通过网路接收键盘录入
         */
        System.out.println("udp接收端");
        DatagramSocket ds = new DatagramSocket(10000);
        while (true) {
            byte[] buf = new byte[1024];
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            ds.receive(dp);//阻塞
            String ip = dp.getAddress().getHostAddress();
            int port = dp.getPort();
            String text = new String(dp.getData(),0,dp.getLength());
            //192.168.0.103 54035 hello udp is coming
            //54035是因为发送端没有明确端口
            System.out.println(ip + " " + port + " " + text);
        }

    }

}
public class UDPSend2 {

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

        /*
         * 键盘录入发送
         */
        System.out.println("udp发送端");
        DatagramSocket ds = new DatagramSocket(9999);
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        String line = null;
        while ((line = bufr.readLine()) != null) {
            if (line.equals("over"))
                break;
            byte[] buf = line.getBytes();
            DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.103"),10000);
            ds.send(dp);
        }
        ds.close();
    }

}
public class UDPChatTest {

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

        /*
         * 通过UDP实现群聊程序
         * 这个程序中既有收又有发,需要同时执行,需要使用多线程技术
         * 一个线程负责发,一个线程负责收
         */
        DatagramSocket sendSocket = new DatagramSocket();
        DatagramSocket receSocket = new DatagramSocket(10002);
        Send send = new Send(sendSocket);
        Rece rece = new Rece(receSocket);
        Thread t1 = new Thread(send);
        Thread t2 = new Thread(rece);
        t1.start();
        t2.start();
    }

}
//发送任务
class Send implements Runnable {

    private DatagramSocket ds;

    public Send(DatagramSocket ds) {
        super();
        this.ds = ds;
    }

    @Override
    public void run() {

        try {
            BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
            String line = null;
            while ((line = bufr.readLine()) != null) {
                byte[] buf = line.getBytes();
                DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.103"),10002);
                ds.send(dp);
                //将886也发送出去
                if (line.equals("886"))
                    break;
            }
            ds.close();
        } catch (IOException e) {

        }

    }

}
//接收任务
class Rece implements Runnable {

    DatagramSocket ds;

    public Rece(DatagramSocket ds) {
        super();
        this.ds = ds;
    }

    @Override
    public void run() {

        while (true) {
            try {
                byte[] buf = new byte[1024];
                DatagramPacket dp = new DatagramPacket(buf, buf.length);
                ds.receive(dp);//阻塞
                String ip = dp.getAddress().getHostAddress();
                int port = dp.getPort();
                String text = new String(dp.getData(),0,dp.getLength());
                System.out.println(ip + " " + port + " " + text);
                if (text.equals("886"))
                    System.out.println(ip + "::离开聊天室");
            } catch (IOException e) {

            }
        }
    }


}
//输入+输出
123
192.168.0.103 62775 123
23
192.168.0.103 62775 23

先启动服务器端,再启动客户端

public class TCPServer {

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

        /*
         *获取客户端的数据并显示在屏幕上 
         *1.创建服务端的socket,明确端口,监听一个端口
         *2.服务端只要获取到连接过来的客户端就可以和指定的客户端通信了
         *3.通过获取客户端的读取流对象读取客户端发来的数据
         *4.显示在屏幕上
         *5.关闭资源
         */
        System.out.println("服务端运行");
        ServerSocket ss = new ServerSocket(10003);
        Socket s = ss.accept();
        String ip = s.getInetAddress().getHostAddress();
        //192.168.0.103::connect
        System.out.println(ip + "::connect");
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        //hello tcp
        System.out.println(text);
        s.close();
        //服务端一般不关闭,这个是为了测试
        ss.close();
    }

}
public class TCPClient {

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

        /*
         * 通过TCP传输数据给服务器
         * 1.建立TCP的客户端Socket,明确服务端的地址和端口
         * 2.如果通道建立成功就会出现socket io流
         *   客户端需要做的就是获取socket流中的输出流将数据发送目的地服务端
         * 3.通过socket输出流将数据发送
         * 4.关闭资源
         */
        System.out.println("客户端运行");
        Socket s = new Socket("192.168.0.103",10003);
        OutputStream out = s.getOutputStream();
        out.write("hello tcp".getBytes());
        s.close();
    }

}

有可能有并发访问的问题,这个例子只有一个发送,一个接收,显示不出来

public class TCPClient2 {

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

        /*
         * 实现客户端和服务端的收发过程
         */
        System.out.println("客户端启动");
        Socket s = new Socket("192.168.0.103",10004);
        OutputStream out = s.getOutputStream();
        out.write("服务端,我来了".getBytes());
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(text);
        s.close();
    }

}
public class TCPServer2 {

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

        System.out.println("服务端启动");
        ServerSocket ss = new ServerSocket(10004);
        Socket s = ss.accept();
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String text = new String(buf,0,len);
        System.out.println(text);
        OutputStream out = s.getOutputStream();
        out.write("客户端,我已收到".getBytes());
        s.close();
        ss.close();
    }

}

网络编程练习

客户端通过键盘录入发送数据到服务端,服务端将接收到的数据显示到屏幕上的同时,将这些数据转成大写发回给客户端,客户端录入的是over时,大写转换结束

public class TransServer {

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

        System.out.println("服务端启动");
        //1.创建服务端socket,明确端口
        ServerSocket ss = new ServerSocket(10006);
        while (true) {
            //获取客户端对象
            Socket s = ss.accept();
            System.out.println(s.getInetAddress().getHostAddress() + "......connected");
            //2.源:socket输入流,读取客户端发过来的数据
            BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
            //3.目的:socket输出流,将转成大写的数据发送给客户端
            PrintWriter out = new PrintWriter(s.getOutputStream(),true);
            //4.频繁的读写操作
            String line = null;
            while ((line = bufIn.readLine()) != null) {
                if ("over".equals(line))
                    break;
                System.out.println(line);
                //转成大写,发回给客户端
                out.println(line.toUpperCase());
            }
            //5.关闭客户端
            s.close();
        }
    }

}
public class TransClient {

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

        System.out.println("客户端启动");
        //1.创建socket,明确地址和端口
        Socket s = new Socket("192.168.0.102",10006);
        //2.源,键盘录入,获取需要转换的数据
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        //3.目的,网络,socket输出流
        //将字符流装换为字节流,省得自己转换,如调用getbytes()方法
        /*
         * OutputStream out = s.getOutputStream();
         * OutputStreamWriter osw = new OutputStreamWriter(out);
         */
        //外面又套一层,其实这种方法都比较低效,用PrintWriter即可
        /*
         * OutputStream out = s.getOutputStream();
         * BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(out));
         */
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //4.源,socket读取流,读取服务端发回来的大写数据
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        //5.目的,客户端显示器,将大写数据显示出来
        //6.频繁的读写操作
        String line = null;
        while ((line = bufr.readLine()) != null) {
            out.println(line);
            if ("over".equals(line))
                break;
            String upperText = bufIn.readLine();
            System.out.println(upperText);
        }
        //7.关闭资源
        s.close();
    }

}

上传文件

public class UploadTextServer {

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

        System.out.println("上传文本服务端启动");
        //1.创建服务端socket,明确端口
        ServerSocket ss = new ServerSocket(10007);
        while (true) {
            //获取客户端对象
            Socket s = ss.accept();
            System.out.println(s.getInetAddress().getHostAddress() + "......connected");
            //2.源:socket输入流,读取客户端发过来的数据
            BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
            //3.目的:文件
            PrintWriter pw = new PrintWriter(new FileWriter("tempfile\\server.txt"),true);
            //4.频繁的读写操作
            String line = null;
            while ((line = bufIn.readLine()) != null) {
                pw.println(line);
            }
            //5.发回给客户端上传成功字样
            PrintWriter out = new PrintWriter(s.getOutputStream(),true);
            out.println("上传成功");

            //6.关闭客户端
            s.close();
        }
    }

}
public class UploadTextClient {

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

        System.out.println("上传文件客户端启动");
        //1.创建socket,明确地址和端口
        Socket s = new Socket("192.168.0.102",10007);
        //2.源,读取文本文件,需要转换的数据
        BufferedReader bufr = new BufferedReader(new FileReader("tempfile\\client.txt"));
        //3.目的,网络,socket输出流
        PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        //4.频繁的读写操作
        String line = null;
        while ((line = bufr.readLine()) != null) {
            out.println(line);
        }
        //给服务端发送一个结束标记,这个标记是约定标记有点麻烦,可以更简单
        //out.println("over");
        //向服务端发送了结束标记,可以让服务端结束读取的动作
        s.shutdownOutput();
        //5.源,socket读取流,读取服务端发回来的上传成功信息
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String info = bufIn.readLine();
        System.out.println(info);
        //6.关闭资源
        bufr.close();
        s.close();
    }

}

正则表达式

正则对字符串的常用功能操作
1.匹配 2.切割 3.替换 4.获取

public class RegxDemo {

    public static void main(String[] args) {

        /*
         * 对QQ号进行校验
         * 要求:5-15位,0不可以开头,必须都是数字
         */
        Scanner in = new Scanner(System.in);
        /*
         * 15613
         * 15613::true
         * 01651
         * 01651::false
         */
        while (in.hasNext()) {
            String qq = in.next();
            boolean bool = qq.matches("[1-9][0-9]{4,14}");
            System.out.println(qq + "::" + bool);
        }
    }

}

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

public class RegexDemo {

    public static void main(String[] args) {

        Scanner in = new Scanner(System.in);
        //匹配,使用String类中的matches
        functionDemo_1();
        //切割,使用String类中的split
        functionDemo_2();
        //替换,使用String类中的replaceAll(regex,string)
        functionDemo_3();
        //其他三个功能内部最终使用的都是Pattern正则表达式对象
        //现在需要其他功能时,字符串String类中没有对应的方法,只能找Pattern对象
        functionDemo_4();
    }

    private static void functionDemo_1() {
        String str = "18600001991";
        //第一位是1,第二位是138其中的一个,剩下的是9个0-9的数字
        String regex = "1[138]\\d{9}";
        boolean flag = str.matches(regex);
        //true
        System.out.println(flag);
    }

    private static void functionDemo_2() {
        String str = "zhangsan,lisi,wangwu";
        String regex = ",";
        String[] strs = str.split(regex);
        //zhangsan
        //lisi
        //wangwu
        for (String string : strs) {
            System.out.println(string);
        }
        str = "zhangsan    lisi  wangwu";
        //空格加+号,一个或多个空格切
        regex = " +";
        strs = str.split(regex);
        //zhangsan
        //lisi
        //wangwu
        for (String string : strs) {
            System.out.println(string);
        }
        str = "zhangsan.lisi.wangwu";
        //想用.分割必须写成\\.不然所有的字符都是切割符
        regex = "\\.";
        strs = str.split(regex);
        for (String string : strs) {
            System.out.println(string);
        }
        //正则规则的复用,想复用,先封装。正则封装用()完成
        //封装完成后由编号,从1开始。规则中被()封装的称之为组。直接通过编号就可以调用对应的组
        //调用方式直接写已有的组的编号见面加上\\,如()\\1,使用已有的第一组内容。原则,先有组,才可以使用对应的编号调用
        str = "ertkkkkyxgd###lfd";
        regex = "(.)\\1+";
        strs = str.split(regex);
        //ert
        //yxgd
        //lfd
        for (String string : strs) {
            System.out.println(string);
        }
    }

    private static void functionDemo_3() {
        String str = "dfdsf###dfdsf####lkg";
        //不能用$符替换,因为$符有特殊含义
        str = str.replaceAll("(.)\\1+", "&");
        //dfdsf&dfdsf&lkg
        System.out.println(str);
        str = "dfsdl@@@klmlk###";
        str = str.replaceAll("(.)\\1+", "$1");
        //dfsdl@klmlk#
        System.out.println(str);
        //将电话号变成135****1111;
        str = "13500001111";
        str = str.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
        //135****1111
        System.out.println(str);
        str = "fefldnflk489415611fdgdfd445151";
        //将连续5个以上数字变成***,所以有的人发现网站屏蔽后这样写,衣二三
        str = str.replaceAll("\\d{5,}", "***");
        //fefldnflk***fdgdfd***
        System.out.println(str);
    }

    /*
     * Pattern对象的使用原理
     * 1.将正则表达式字符串编译成正则对象pattern
     * 2.通过pattern对象获取Matcher对象(匹配器对象)
     * 3.通过匹配器对象对字符串进行规则的匹配,结果都在匹配器中
     * 4.通过匹配器对象的功能获取结果
     * 范例代码:
     * Pattern p = Pattern.compile("a*b");
     * Matcher m = p.matcher("aaaaab");
     * boolean b = m.matches();
     */
    private static void functionDemo_4() {
        String str = "da jia zhu yi la,ming tian fang jia le!";
        String regex = "[a-zA-Z]{3}";//取出由3个字母组成的单词
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(str);
        //jia
        //zhu
        //min
        //tia
        //fan
        //jia
        while (m.find()) {
            System.out.println(m.group());
        }
        regex = "\\b[a-zA-Z]{3}\\b";
        p = Pattern.compile(regex);
        m = p.matcher(str);
        //3::jia::6
        //jia
        //7::zhu::10
        //zhu
        //32::jia::35
        //jia
        while (m.find()) {
            System.out.println(m.start() + "::" + m.group() + "::" + m.end());
            System.out.println(str.substring(m.start(), m.end()));
        }
    }

}

正则表达式练习

public class RegexDemo2 {

    public static void main(String[] args) {
        test1();
        test2();
        test3();
    }

    private static void test1() {
        /*
         * 我我我....我要学学....软...软件....件
         * 变成我要学软件
         */
        String str = "我我我....我要学学....软...软件....件";
        //一个或者多个点替换成空字符串
        str = str.replaceAll("\\.+", "");
        //我我我我要学学软软件件
        System.out.println(str);
        str = str.replaceAll("(.)\\1+", "$1");
        //我要学软件
        System.out.println(str);
    }

    private static void test2() {
        /*
         * 23.12.10.5    192.168.100.223  3.3.3.3  10.10.10.10
         * 将ip按照顺序排序
         * 将ip切割再按照字典序排序是错误的,因为比较的位数不一样,应该都补足3位再进行比较
         * 如何补0?每一段的位数不同,补零的个数也不同,按所需的最多的零补,每一段都补2个零
         * 有的地址段多了,取每一段的最后三位
         */
        String ipStr = "23.12.10.5    192.168.100.223  3.3.3.3  10.10.10.10";
        ipStr = ipStr.replaceAll("(\\d+)", "00$1");
        //0023.0012.0010.005    00192.00168.00100.00223  003.003.003.003  0010.0010.0010.0010
        System.out.println(ipStr);
        ipStr = ipStr.replaceAll("0*(\\d{3})", "$1");
        //023.012.010.005    192.168.100.223  003.003.003.003  010.010.010.010
        System.out.println(ipStr);
        String[] ips = ipStr.split(" +");
        Arrays.sort(ips);
        //3.3.3.3
        //10.10.10.10
        //23.12.10.5
        //192.168.100.223
        for (String ip : ips) {
            //去除前导零并且输出
            System.out.println(ip.replaceAll("0*(\\d+)", "$1"));
        }
    }

    private static void test3() {
        /*
         * 对邮件地址进行校验
         */
        String mail = "itcast2013@itcast.cn";
        //数字字母一次或者多次+@符号+字母或者数字+.+字母2次到3次
        String regex = "[a-zA-Z_0-9]+@[a-zA-Z0-9]+\\.+[a-zA-Z]{2,3}";
        boolean b = mail.matches(regex);
        //itcast2013@itcast.cn::true
        System.out.println(mail + "::" + b);
        //有可能有多个后缀
        mail = "itcast2013@itcast.com.cn";
        //限制后缀为1次到3次
        regex = "[a-zA-Z_0-9]+@[a-zA-Z0-9]+(\\.+[a-zA-Z]{2,3}){1,3}";
        b = mail.matches(regex);
        //itcast2013@itcast.com.cn::true
        System.out.println(mail + "::" + b);
        //比较笼统的方式
        regex = "\\w+@\\w+(\\.\\w+)+";
    }

}
public class NetSpider {

    public static void main(String[] args) throws IOException {
        /*
         * 网络爬虫,其实就是一个应用程序,获取网络中的指定信息(符合指定规则的信息)
         * 网络中的邮件地址
         */
        File file = new File("tempfile\\mail.html");
        String regex = "\\w+@\\w+(\\.\\w+)+";
        List<String> list = getMail(file,regex);
        //list 515123@163.com
        //list 28651321@qq.com
        for (String mail : list) {
            System.out.println("list " + mail);
        }
        String urlStr = "http://bbs.tianya.cn/post-444-48585-1.shtml";
        list = getMailsByNet(urlStr, regex);
        //url 2112176384@qq.com
        //url 1337976723@qq.com
        //url 2640326225@qq.com
        for (String mail : list) {
            System.out.println("url " + mail);
        }
    }
    //基于网络的爬虫
    public static List<String> getMailsByNet(String urlStr,String regex) throws IOException {
        List<String> list = new ArrayList<>();
        //1.将urlStr封装成url对象
        URL url = new URL(urlStr);
        //2.打开链接
        URLConnection conn = url.openConnection();
        //3.获取读取流
        InputStream in = conn.getInputStream();
        BufferedReader bufr = new BufferedReader(new InputStreamReader(in));
        //4.将正则表达式编译成对象
        Pattern p = Pattern.compile(regex);
        String line = null;
        while ((line = bufr.readLine()) != null) {
            Matcher m = p.matcher(line);
            while (m.find()) {
                list.add(m.group());
            }
        }
        bufr.close();
        return list;
    }
    //基于本地的爬虫
    private static List<String> getMail(File file, String regex) throws IOException {
        List<String> list = new ArrayList<>();
        //1.读取文件
        BufferedReader bufr = new BufferedReader(new FileReader(file));
        //2.将正则规则编译成对象
        Pattern p = Pattern.compile(regex);
        String line = null;
        while ((line = bufr.readLine()) != null) {
            Matcher m = p.matcher(line);
            while (m.find()) {
                list.add(m.group());
            }
        }
        bufr.close();
        return list;
    }

}

反射

反射技术:动态的获取指定的类以及动态的调用类中的内容应用程序已经写好,后期出现的接口子类无法在该应用程序中用new创建对象该怎么办?
既然子类不确定,可以通过对外提供配置文件的形式,将不确定的信息存储到配置文件中即可,该程序只要之前写好如何读取配置文件信息即可。把具体实现的子类的名称定义到配置文件中。如果存储了指定的子类名,就根据具体的名称找该类并进行加载和对象的创建,这些动作都在前期定义软件时写好的。没有类之前就将创建对象的动作完成了。这就是动态获取指定的类,并使用类中的功能,这就是反射技术。反射技术的出现大大提高了程序的扩展性

public class Person {

    private String name;
    private int age;

    public Person() {
        super();
    }

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

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

}
public class ReflectDemo {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        /*
         * 要想获取字节码文件中的成员,必须先获取字节码文件对象
         * 获取字节码文件对象的方式
         * 1.通过Object类的getClass方法
         * 虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass()方法
         * 2.使用任意数据类的一个静态成员class,所有的数据类型都具备的一个属性
         * 好处,不用new对象,但是,还需要使用具体的类
         * 3.使用Class类中的forName方法,通过给定类名来获取对应的字节码文件对象
         * 只要知道类的名字就可以,获取对应的字节码文件直接由forName方法自动完成
         * 这就是反射技术获取字节码文件的方式
         */
        getClass_1();
        getClass_2();
        getClass_3();
    }

    private static void getClass_1() {

        Person p1 = new Person();
        Person p2 = new Person();
        Class c1 = p1.getClass();
        Class c2 = p2.getClass();
        //true
        System.out.println(c1 == c2);
        //practice8.Person
        System.out.println(c1.getName());
    }

    private static void getClass_2() {

        Class clazz = Person.class;
    }

    private static void getClass_3() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //必须加上包名
        String className = "practice8.Person";
        Class clazz = Class.forName(className);
        System.out.println(clazz);
        /*
         * 通过newInstance()就可以创建字节码对象所表示的类的实例
         * 通常被反射的类都会有提供空参数的构造函数
         * 没有对应的构造函数,会报InstantiationException
         * 如果有提供,但是权限不够,会报IllegalAccessException(就是把Person类构造函数前面的public去掉)
         */ 
        Object obj = clazz.newInstance();
        /*
         * 和上面是一个效果
         * Person person = new Person();
         * 1.加载Person类,并将Person类封装成字节码文件对象
         * 2.通过new创建Person对象
         * 3.调用构造函数对对象初始化
         */
        //practice8.Person@139a55    对象类型和哈希值
        System.out.println(obj);
    }

}
public class ReflectDemo2 {

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

        /*
         * 通过指定的构造函数初始化对象
         * 1.获取字节码文件对象
         * 2.再获取给定的构造函数
         * 3.通过构造函数初始化对象
         */
        getConstructorDemo();
    }

    private static void getConstructorDemo() throws Exception {

        String className = "practice8.Person";
        Class clazz = Class.forName(className);
        //获取指定的构造器,获取Person类中2个参数string,int的构造函数
        Constructor cons = clazz.getConstructor(String.class,int.class);
        //有了构造器对象后,通过构造器对象来初始化类对象
        Object obj = cons.newInstance("wangwu",23);
        //Person [name=wangwu, age=23]
        System.out.println(obj);
    }

}
public class ReflectDemo3 {

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

        //获取字段
        getFieldDemo();
        //获取方法
        getMethodDemo();
        //获取静态方法
        getMethodDemo2();
    }

    private static void getFieldDemo() throws Exception {
        String className = "practice8.Person";
        Class clazz = Class.forName(className);
        String fieldName = "age";
        //获取的是公共字段
        //Field field = clazz.getField(fieldName);
        Field field = clazz.getDeclaredField(fieldName);
        //private int practice8.Person.age
        System.out.println(field);
        //getXXX获取类中公共成员
        //getDeclaredXXX获取本类中已有的成员
        Object obj = clazz.newInstance();
        field.setAccessible(true);//取消权限检查,暴力访问,一般不访问私有
        field.set(obj, 30);//IllegalAccessException:age字段是私有的
        //30
        System.out.println(field.get(obj));
    }

    private static void getMethodDemo() throws Exception {
        String className = "practice8.Person";
        Class clazz = Class.forName(className);
        String methodName = "show";
        Method method = clazz.getMethod(methodName, String.class, int.class);
        Object obj = clazz.newInstance();
        //调用Person的函数会输出   show run.... name wangwu age 20
        method.invoke(obj, "wangwu",20);
    }

    private static void getMethodDemo2() throws Exception {
        String className = "practice8.Person";
        Class clazz = Class.forName(className);
        String methodName = "staticShow";
        Method method = clazz.getMethod(methodName, null);
        //调用Person的函数会输出 static show run....
        method.invoke(null, null);
    }

}

反射练习

public class NoteBook {

    /*
     * 笔记本类
     */
    //运行
    public void run() {
        System.out.println("notebook run");
    }
    //使用USB的设备,多态的体现
    public void useUSB(USB usb) {
        if (usb != null) {
            usb.open();
            usb.close();
        }
    }
}
public interface USB {
    /*
     * 为笔记本类设计的USB接口
     */
    void open();
    void close();
}
public class MouseByUSB implements USB {

    /*
     * 实现USB接口的鼠标
     */

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

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

}
public class KeyByUSB implements USB {

    /*
     * 实现USB接口的键盘
     */

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

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

}

usb.properties(tomcat是用XML文件进行配置)

usb1=practice9.MouseByUSB
usb2=practice9.KeyByUSB
public class NoteBookMain {

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

        /*
         * 案例1:
         * 阶段1:笔记本电脑运行 NoteBook run()
         * 阶段2:想要使用一些外围设备,比如鼠标,键盘
         * 为了提高笔记本的扩展性,应该降低这些设备和笔记本的耦合性,需要接口
         * 只需要在设计之初定义一个接口,而且笔记本在使用这个接口
         * 后期有了USB设备后,需要不断new对象才可以用,每一次都要修改代码
         * 能不能不修改代码就使用后期的设备
         * 设备不明确,而前期还要对其进行对象的建立,需要反射技术
         * 对外提供一个配置文件
         */
        NoteBook book = new NoteBook();
        //函数运行输出 notebook run
        book.run();
        //book.useUSB(null);
        //函数输出mouse open
        //mouse close
        //book.useUSB(new MouseByUSB());
        //通过反射的方法重新设计应用程序,以提高更好的扩展性
        File configFile = new File("tempfile\\usb.properties");
        if (!configFile.exists())
            configFile.createNewFile();
        FileReader fr = new FileReader(configFile);
        //为了获取其中的键值信息方便,建立properties
        Properties prop = new Properties();
        prop.load(fr);
        for (int i=1; i<=prop.size(); i++) {
            String className = prop.getProperty("usb" + i);
            Class clazz = Class.forName(className);
            USB usb = (USB)clazz.newInstance();
            //函数输出
            //mouse open
            //mouse close
            //key open
            //key close
            book.useUSB(usb);
        }
        fr.close();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java识堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值