Java知识点Java8新特性

JDK8

lambda :简化代码

lambda λ 组成 :

  • 逗号隔开的参数列表 (x,x,x) ()
  • 箭头符号 ->
  • 方法体 (λ代码块)

示例

1583460022040

package lambda;
public class Demo01 {
    public static void test01(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("run....");
            }
        }).start(); 
    }

    public static void test02(){
        new Thread(
//           ()  -> { System.out.println("run....");  }
             ()  ->  System.out.println("run....")
        ).start(); ;
    }

    public static void main(String[] args) {
//        test01() ;
          test02() ;

    }
}
        new Thread(
                ()  ->  System.out.println("run....")
        ).start(); ;

以上述为例, new Thread( )中是一个接口、抽象类。但是为了避免 不能区分 lambda到底是重写的哪个方法,语法上lambda要求 重写的 接口、抽象类中 有且只能有一个 抽象方法

仔细观察,lambda重写的接口、抽象类中 会标识一个@FunctionalInterface,称为函数式接口。

函数式接口:标注@FunctionalInterface,有且只有一个 抽象方法。

lambda表达式重写的 必须是 函数式接口(或者 只有一个抽象方法的抽象类)

函数式接口要注意以下几点:

  • 即使没有标注@FunctionalInterface,但是只有一个抽象方法,也称之为函数式接口(接口中的方法的默认修饰符为public abstract
  • 特殊情况:如果某个接口中有多个抽象方法,但只有1个抽象方法是本接口新定义的,其他抽象方法和Object中已有的方法重复,那么该接口仍然是函数式接口。

    package lambda;
    
    @FunctionalInterface
    public interface MyInterface  {//everything is an Object
    public abstract void a() ;//本接口新定义的抽象
    
    public abstract String toString() ;//和Object中重名(实际会调用Object中的toString())
    public abstract boolean equals(Object obj) ;//和Object中重名
    
    //重点:toString()和equals()看似是抽象方法,实则是 有方法体的具体方法
    }
    
    class MyInterfaceImpl implements  MyInterface{
    @Override
    public void a() {
        System.out.println("a...");
    }
    }
    class TestMyInterface{
    public static void main(String[] args) {
    
        MyInterfaceImpl impl = new MyInterfaceImpl();
        impl.a();
        impl.equals("")  ;
        impl.toString();
    }
    }
    
    

MyInterface中的a()方法是 自己定义的,而其他equals()、toString()方法可以理解为 从Object中继承而来,因此 MyInterface虽然显示了3个抽象,但其实只有1个抽象。

() { return }

函数式接口从哪来?

  • JDK自带 (很多存在于java.util.function包中)

​ 四大核心函数式接口

  • 有参,无返回值 (消费型:传给你一个参数,你把他给弄没了)

     @FunctionalInterface
     public interface Consumer<T> {
         void accept(T t);
         ...
     }
    
  • 无参,有返回值(供给型:什么都没给你,你却给我了一个东西)

    @FunctionalInterface
    public interface Supplier<T> {
    T get();
      }
    
    • 有参,有返回值 (函数型:)

      @FunctionalInterface
      public interface Function<T, R> {
      R apply(T t);
      ...
      }
      
    • 断言式接口

      @FunctionalInterface
      public interface Predicate<T> {
      boolean test(T t);
      ...
      }
      
  • 自定义

lambda表达式的使用方式

方式一

​ 函数式接口 引用名 = lambda表达式 ;(先实现在使用)

​ Predicate p = num -> num < 10 ;

    public static void test01(){
        //   boolean test(T t);
        Predicate<Integer> p = (num) ->  {return num < 10;} ;
        //Predicate<Integer> p = num ->  num < 10  ;
        System.out.println( p.test( 3  )   );
    }
    public static void test02(){
        //相当于将 MyMath中的add()方法进行了具体的实现
//        MyMath math =  (int n1,int n2) -> {  return  n1+n2 ;} ;
        MyMath math =  (n1,n2) -> {  return  n1+n2 ;} ;
        System.out.println(   math.add(1,100  )       );
    }

示例

package lambda;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Demo02 {
    public static void test01(){
        //   boolean test(T t);
//        Predicate<Integer> p = (num) ->  {return num < 10;} ;
        Predicate<Integer> p = num ->  num < 10  ;
        System.out.println( p.test( 3  )   );
    }

    public static void test02(){
        //相当于将 MyMath中的add()方法进行了具体的实现
//        MyMath math =  (int n1,int n2) -> {  return  n1+n2 ;} ;
         //lambda自带类型推断机制,因此参数的类型 可以省略
        MyMath math =  ( n1,n2) ->     n1+n2  ;
        System.out.println(   math.add(1,100  )       );
    }


    public static void test03(){
        // void accept(T t);
        Consumer<String> c = (x) ->  System.out.println("吃:"+x) ;

        c.accept("苹果");
    }

    public static void test04(){

        Supplier<Integer> supplier = ()->  (int)(Math.random()*9000+1000) ;
        System.out.println(   supplier.get() );
    }


    public static void test05(){

        Function<String,String> f = (s) -> s.toUpperCase() ;
        System.out.println(  f.apply("hello world"));
    }

        public static void main(String[] args) {

        test05();
    }
}

方式二

new Thread(
    ()  ->  System.out.println("run....")
).start(); ;

将lambda表达式所代表的函数式接口,作为一个方法的参数存在。

理解:方法B( 方法A ):函数式编程。scala javascript本身就支持函数式编程。

形式:方法( 函数式接口 )

1583469965192

package lambda;

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class Demo02 {

    public static void test01(){
        //   boolean test(T t);
//        Predicate<Integer> p = (num) ->  {return num < 10;} ;
        Predicate<Integer> p = num ->  num < 10  ;
        System.out.println( p.test( 3  )   );
    }

    public static void test02(){
        //相当于将 MyMath中的add()方法进行了具体的实现
//        MyMath math =  (int n1,int n2) -> {  return  n1+n2 ;} ;
        MyMath math =  (n1,n2) ->     n1+n2  ;
        System.out.println(   math.add(1,100  )       );
    }


    public static void test03(){
        // void accept(T t);
        Consumer<String> c = (x) ->  System.out.println("吃:"+x) ;

        c.accept("苹果");
    }

    public static void test04(){

        Supplier<Integer> supplier = ()->  (int)(Math.random()*9000+1000) ;
        System.out.println(   supplier.get() );
    }


    public static void test05(){
        Function<String,String> f = (s) -> s.toUpperCase() ;
        System.out.println(  f.apply("hello world"));
    }

    public static void test06(){
        String result = upper( (x)-> x.toUpperCase()  ,"hello");
        System.out.println(result);
    }
                                                // fun:函数的逻辑   ,str:hello
    public static String upper( Function<String,String> fun ,String str ){
        return  fun.apply( str  ) ;
    }

    public static void test07(){
        myPredicate( (x) -> x>18   ,  10);

    }
    public static void myPredicate(Predicate<Integer> pre,  Integer num   ){
        System.out.println(   pre.test( num ) );
    }



        public static void main(String[] args) {

        test07();
    }
}

JDK8其他新特性简介

接口 的默认方法与静态方法

在Java 8中,可以使用default关键字在接口中定义默认方法,并提供默认的实现。之后,该接口的所有实现类都会默认地使用该实现方法。当然,实现类也可以对该默认方法进行重写。例如:

public interface MyInterface {
    default String myFunction() {
        return "hello world";
    }
}

此外,还可以在接口中定义静态方法,并提供默认实现。例如:

public interface MyInterface {
     static String myFunction() {
        return "hello world";
     }
}

方法引用

Java 8还允许使用::来引用一个已经存在的方法,其语法如下:

类名::方法名

注意:只写方法名即可,不需要写括号。

具体地讲,共有以下四种类型的引用。

类 型 示 例
引用静态方法 ContainingClass::staticMethodName
引用某个对象的实例方法 ContainingObject::instanceMethodName
引用某个类型的任意对象的实例方法 ContainingType::methodName
引用构造方法 ClassName::new
        ArrayList<String> list = new ArrayList<>() ;
      //  list.add(...);   参数:String,返回值:boolean

        Predicate<String> pre = list::add ;   // pre.test(  ):参数:String,返回值:boolean
        pre.test("a") ;
        pre.test("b") ;

        System.out.println(list);

1583471864090

重复注解

自从Java 5引入了注解以后,注解就被广泛应用于各个框架之中。但Java 5引入的注解存在一个问题:在同一个地方不能多次使用同一个注解。而Java 8就打破了这个限制,引入了重复注解的概念,允许在同一个地方多次使用同一个注解。

在Java 8中使用@Repeatable注解定义重复注解,代码示例如下:

@Repeatable(Authorities.class)
public @interface Authority {
     String role();
}

public @interface Authorities {
    Authority[] value();
}

public class RepeatAnnotationUseNewVersion {

    @Authority(role="Admin")
    @Authority(role="Manager")
    public void doSomeThing(){ }

}

在创建重复注解Authority时,加上@Repeatable并指向用于存储的注解Authorities。之后,在使用的时候,就可以重复使用Authority注解。

其他

Java 8 还提供了Nashorn引擎便于在JVM上运行JavaScript代码,提供了新的Date API,提供了对高并发的新支持,类依赖分析器jdeps……

集合

Collection:存储的数据是 不唯一、无序的对象

List:存储的数据是 不唯一、有序的对象

Set:存储的数据是 唯一、无序的对象

唯一:不能重复

有序:不是排序;是输入顺序 是否与 输出顺序一致的。

HashXxx:底层借助了“哈希表” 的数据结构;默认不支持排序 TreeXxx:底层借助了“红黑色”的数据结构;默认支持排序

set中的remove()为什么只能根据 内容删?不能根据下标删?

package col;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
public class Demo01 {
    public static void main(String[] args) {
        Collection list = new ArrayList();
        list.add("中国") ;
        list.add("美国") ;
        list.add("俄罗斯") ;
        System.out.println(list);


        LinkedList list2 = new LinkedList();
        list2.add("hello") ;
        list2.add("world") ;
        //LinkedList独有的方法
        list2.addLast("wh");
        list2.addFirst("hw");

//        list2.remove(1) ;
        list2.remove("world");

        System.out.println(list2);

        HashSet set = new HashSet() ;
        set.add("aa") ;
        set.add("bb") ;
        set.add("cc") ;
        set.remove("bb") ;//因为 set是无序的,因此无法根据“下标”删。只能根据内容删
        System.out.println(set);
    }
}

map中的key是唯一的;value是不唯一的

删除:

Collection中的类(List、Set),删除的返回值是 boolean;

Map中的类,是根据Key删除,返回值是value.

package col;

import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

public class Demo02 {
    public static void main(String[] args) {
        HashMap map = new HashMap() ;
        map.put( "s01","张三")  ;//key:学号 ,value:姓名
        map.put( "s02","李四")  ;
        map.put( "s03","王五")  ;
        map.put( "s04","王五")  ;

        System.out.println(map);

        Object v = map.get("s01");//根据key,找到value

        System.out.println( v);

        System.out.println(map.size());
        //判断是否 包含了指定的Key
        System.out.println(    map.containsKey("s01")              );
        //判断是否 包含了指定的value
        System.out.println(    map.containsValue("王五")              );

        //将Map转为单值集合
        //转为只包含了key的单值集合

        Set set = map.keySet();//为什么是set,不是list?因为map中,key是唯一的
        System.out.println(set);

        //转为只包含了value的单值集合
        Collection values = map.values();
        System.out.println(values);

        Object a = map.remove("s01");//删除的返回值,就是删除的 value对象
        System.out.println(map);
        System.out.println(a);

    }
}

如何遍历集合?list set map

(1)循环for(普通for[只适用于‘有序’的集合],增强for[建议])

(2)迭代器

public interface Iterator<E> {...}

package col;

import java.util.*;

public class Demo03 {
    public static void main(String[] args) {

        List list = new ArrayList() ;
        list.add("aa") ;
        list.add("bb") ;
        list.add("cc") ;
        //普通for
        for(int i=0;i<list.size();i++){
            System.out.println(   list.get(i));
        }
        System.out.println("增强for");
        //增强for
        for(Object o :list){
            System.out.println(o);

        }

        Set set = new HashSet() ;
        set.add("A");
        set.add("b");
        set.add(1);
        set.add("D");
        //普通for,不适用于 “无序”的集合
//        for(int i=0;i<set.size();i++){
//            System.out.println(      set.get );
//        }

        for(Object e :   set){
            System.out.println(e);
        }
        System.out.println("迭代器");

        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            Object o = iterator.next();
            System.out.println(o);
        }

    }
}

遍历map:将双值集合 ,转为单值集合。

map -> keySet

map->values


        System.out.println("遍历map。。。");
        Map map = new HashMap() ;
        map.put("s01","Zs") ;
        map.put("s02","ls") ;
        map.put("s03","ww") ;
        Set set1 = map.keySet();//将双值集合 降成单值集合
        for(Object o :set1){
            System.out.println("key:" +o);
            Object v = map.get(o);//map可以根据key,获取value; map.get(key) ->value
            System.out.println("value:"+v);

        }

//        Collection values1 = map.values();
//        Iterator iterator1 = values1.iterator();
//        while(iterator1.hasNext()){
//            System.out.println("value: "+iterator1.next() );
//        }

        System.out.println("通过entry遍历..");
        Set entries = map.entrySet();

        for(Object e :entries){
            Map.Entry et =  (Map.Entry)e ;
           Object k =  et.getKey();
            Object v = et.getValue();
            System.out.println("k-"+k);
            System.out.println("v-"+v);
        }

1583125944732

泛型

Collection、Map接口中都可以使用

自jdk1.5开始提供。

作用:

(1)数据安全

(2)防止类型转换时出错

list.add(默认是Object)

如果加了Double泛型,则自动变成 list.add(double…),返回值类似,会直接返回double类型的数据

简言之,以Double泛型为例。如果不加泛型,则默认操作的是Object类型,如果加了Double泛型,则默认操作的是Double类型。

package col;

import java.util.*;
public class Demo04 {
    public static void main(String[] args) {
//        List<String> list = new ArrayList<String>() ;
        List<String> list = new ArrayList<>() ;//jdk1.7之后提供类型推断
        list.add("a") ;
//        list.add(1) ;
        String s = list.get(0);
        System.out.println(s);

        Set<String> set =new HashSet<>() ;
        set.add("aa") ;

        //Key(学号s01),Value(名次)
        Map<String,Integer> map = new HashMap<>() ;
        map.put("s01",3);
        map.put("s02",1);
        map.put("s03",2);

        Set<Map.Entry<String,Integer>>  entries = map.entrySet();
        for(Map.Entry<String,Integer> entry :entries){
            System.out.println(  entry.getKey());
            System.out.println(  entry.getValue());
        }


        System.out.println("迭代器中...");
        //取key
        Set<String> keys = map.keySet();
        //遍历Key
        Iterator<String> iterator = keys.iterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            Integer value = map.get(key) ;
            System.out.println(key+"--"+value);
        }


    }
}

示例:根据人的名字,查询这个人的全部信息。

package col;

import javax.sound.midi.Soundbank;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
public class Demo05 {
    public static void main(String[] args) {
        Map<String,Person> map = new HashMap<>() ;
        Person p1 = new Person("zs",23,"xa");
        Person p2 = new Person("ls",24,"bj");
        Person p3 = new Person("ww",25,"sh");
        Person p4 = new Person("zl",26,"sz");
        Person p5 = new Person("sq",27,"tj");

        //key:名字    value:人
        map.put("zs",p1);
        map.put("ls",p2);
        map.put("ww",p3);
        map.put("zl",p4);
        map.put("sq",p5);

        System.out.println("请输入人的名字:");
        Scanner in = new Scanner(System.in) ;
        String name = in.next();

        Person person = map.get(name);
        System.out.println(person);//如果不是null,在打印时会调用toString;如果是null,则不调用。
//        System.out.println(person.toString());


//        Set<String> names = map.keySet();
//        //在names中查找name
//        for(String n :names){
//            if(n.equals( name )){
//                Person person = map.get(n);
//                System.out.println(person);
//            }
//        }
    }
}

集合工具类

集合工具类Collections:排序、复制、翻转等操作

数组工具类Arrays 排序、复制、翻转等操作 ,Arrays.sort(数组)

package col;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Demo06 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("w") ;
        list.add("a") ;
        list.add("b") ;
        list.add("x") ;
        list.add("a") ;
        list.add("i") ;

        Collections.sort(list);
        System.out.println(list);

        System.out.println(   Collections.max(list) );
        System.out.println(   Collections.min(list) );

        //二分查法(使用前,必须保证集合元素是 自然有序的)
        System.out.println(  Collections.binarySearch( list,"i")     );
        //混洗,洗牌:打乱已有顺序
        Collections.shuffle(list);
        System.out.println(list);
        Collections.shuffle(list);
        System.out.println(list);

        Collections.reverse(  list);
        System.out.println(list);

        Collections.swap(list,  2,3);
        System.out.println(list);

        Collections.replaceAll(list, "a","A") ;
        System.out.println(list);

        Collections.fill(list,"H");
        System.out.println(list);
    }
}

package col;

import java.util.Arrays;

public class Demo07 {
    public static void main(String[] args) {
        int[] arr = new int[]{3,2,14,6,1};
        Arrays.sort(arr);

        for(int i=0;i<arr.length;i++){
            System.out.println(arr[i]);
        }
//
//        Arrays.fill(arr,5);
//        for(int i=0;i<arr.length;i++){
//            System.out.println(arr[i]);
//        }

        Arrays.sort(arr);
        //通过二分法,在arr中查找2的下标(前提是 数组已经排好序)
        int position = Arrays.binarySearch(arr, 2);
        System.out.println(position);


    }
}

比较器

list.add(3) ; //2 4 3 1 9

list.add(“b”) ; //b c a d w

Collections.sort(list) //能够识别一些自然顺序

但是,如果集合中的元素是 用户自定义对象,如何排序?

Person p1 = new Person(3,“zs”,23,6) ;

Person p2 = new Person(2,“ls”,24,2) ;

Person p3 = new Person(1,“ww”,25,8) ;

list.add(p1);

list.add(p2);

list.add(p3);

Collections.sort(list) //程序 无法知道 ,是根据什么排序? ->自定义比较器, 自定义“比较时,根据学号排”

自定义比较器:Comparable 、Comparator

Comparable: 内部比较器(侵入性,需要修改原有代码)

package col;
public class Person implements Comparable{
    private int id ;
    private String name ;
    private int age ;
    private String city;
    public Person() {
    }
    public Person(int id, String name, int age, String city) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.city = city;
    }
    public Person( String name, int age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", city='" + city + '\'' +
                '}';
    }

    //重写比较器接口中的方法

    /*
        {1,3,5,7  }升序
        {7,5,3,1  }降序
        //输入源   3     1  7  5
           {3}

        返回值:
            1正数:当前对象大,    降序
            -1负数:传入的对象大 ,升序
            0:一样大
         传入一个Person对象和当前对象进行比较
     */
    @Override
    public int compareTo(Object o) {
        Person inputPerson = (Person)o ;
        //根据学号,降序
       int result =  this.id > inputPerson.id ?-1  : (   this.id   ==    inputPerson.id ?0:1 ) ;
       //如果学号相同,再根据姓名升序.  za  zs
        if(result  == 0 ){
            result = this.name .compareTo(      inputPerson.name) ;//调用String已经重写过的compareTo()
        }
        return result;
//        return 1;//当前对象,比传入对象大
    }
}

思路:将比较的对象(Person)实现Comparable接口,重写连的compareTo()方法。在compareTo()中编写比较的逻辑。重点是返回值,-1 ,0,1 ;

Comparator:外部比较器 (无侵入性,不影响原有代码)

1.先定义一个外部比较器

package col;

import java.util.Comparator;

public class MyComparatorWithId implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Student s1 =  (Student)o1 ;
        Student s2 =  (Student)o2 ;
        return   s2.getId() -  s1.getId()  ;
    }
}

2.使用外部比较器

package col;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

//内部比较器
public class Demo08 {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>() ;
        Person p1= new Person(10,"zs",23,"xa") ;
        Person p2= new Person(2,"ls",24,"xa") ;
        Person p3= new Person(2,"zs",25,"xa") ;
        persons.add(p1) ;
        persons.add(p2) ;
        persons.add(p3) ;

        Collections.sort( persons );

        System.out.println(persons);
        System.out.println("-------");
        Student s1 = new Student(10,"zs",23,"xa") ;
        Student s2 = new Student(2,"zs",26,"xa") ;
        Student s3 = new Student(3,"zs",24,"xa") ;
        List<Student> students = new ArrayList<>() ;
        students.add(s1);
        students.add(s2);
        students.add(s3);

        Collections.sort(  students,new MyComparatorWithId() );//给students使用了MyComparatorWithId比较器
        System.out.println(students);

    }
}

List list = new ArrayList<>() ;

包装类(自动装箱、自动拆箱)

evething is an Object,万物皆对象。 属性、方法

自动装箱:基本类型 ->包装类型 ,int ->Integer

自动拆箱:包装类型->基本类型,Integer ->int

java中 将8个基本类型,包装成了8个对象类型(引用类型)。这8个对象类型 就成为包装类

1583138824295

package col;
public class BZL {
    public static void main(String[] args) {
        int i = 10 ;
        Integer j = new Integer(20) ;
        i = j ;//Integer->int  自动拆箱 底层用的是intValue()
        j = i ;//int ->Integer 自动装箱 底层用的是valueOf() ,该方法中有个缓冲区 [-128,127],如果要装箱的数字 在缓冲区范围以内,则直接从缓冲区中取;否则,new Integer()一个新对象
        Integer i1 = 100 ;
        Integer i2 = 100 ;
        Integer i3 = 1000 ;
        Integer i4 = 1000 ;
        System.out.println(i1 == i2);
        System.out.println(i3 == i4);

    }
}

异常

简介

try
{
    //一切正常,执行这里
}catch(Exception e)
{
    //如果不正常,执行这里(异常)
}
int[] nums = new int[3] ;
try
{
    //nums[n]一切正常,执行这里
}catch(Exception e)
{
    // nums[n]如果不正常,执行这里(异常)
}

如果代码出现问题时,使用“异常机制”比 选择结构 更加方便。

异常的继承结构图

image-20200406184011614

运行时异常:代码在编辑(编译阶段)时 不报错,运行时才报错。语法上, 可以选择性处理。

检查异常:代码在编辑(编译阶段)时报错。在编辑时,必须处理。(没有使用try catch包围)

异常重点

try catch finally throws throw

处理异常:try catch 或者 throws

try: 将可能发生异常的代码 用{}包裹起来

catch : 捕获特定类型的异常;捕获时,先写范围小的类型,后写范围大的类型

如果try中的代码的确发生了异常,则程序 不再执行try中异常之后的代码,而是直接跳到cath中执行

public class Demo01 {
    public static void main(String[] args) {
        int[] nums = new int[3] ;
        System.out.println(nums[3]);//运行时异常,选择性处理
//

//       Class.forName("ex.Demo01");//检查异常,强制处理
    }
}

try catch

自己(当前方法)能够处理,使用try catch .

throws

自己(当前方法)不能处理,上交给 上级(方法调用处)处理 ,使用throws .

    public static void test04() throws NullPointerException,ClassNotFoundException{//抛出异常,抛出给上级(方法调用处)
            Object obj = null   ;
            obj.equals("") ;//空指针
            Class.forName("xxx") ;
    }

    public static void main(String[] args) throws Exception{//继续往上抛出异常(JVM)
            test04();//异常
    }

finally

无论正常,还是异常,始终都会执行的代码

不论执行完try,还是执行完catch,最终都会执行finally的代码

1.即使遇到return ,也仍然会执行finally

2.除非虚拟机关闭,才不会执行finally

package ex;

public class Demo02 {

    public static void test01(){

        Object obj = null   ;
        try {
//            System.out.println("111");
            obj.equals("");//可能产生异常的代码
//            System.out.println("222");
//            System.out.println("正常时,关闭资源。。。");
        }catch (NullPointerException e){//捕获特定类型的异常
//            System.out.println("异常。。。");
//            System.out.println("发生了空指针异常。。");
//            System.out.println("异常时,关闭资源。。。");
        }finally{
            System.out.println("无论正常,还是异常,始终都会执行的代码。。。");
        }
    }

    public static int test02(){
        try{
            Object obj = null   ;

            System.exit(1);//关闭jvm
            obj.equals("") ;
            return 1 ;
        }catch (NullPointerException e){
                return 0 ;
        }finally{
            System.out.println("finally...");
        }
    }

    public static void main(String[] args) {
        test02() ;



    }
}

多个catch块

   public static void test03(){
        try{
            Class.forName("xxx") ;
            Object obj = null   ;
            obj.equals("") ;//空指针

            int[] nums = new int[3] ;
           nums[3] = 3 ;//数组越界异常

        }catch (NullPointerException e){//捕获空指针
            System.out.println("空指针异常..");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("数组越界异常...");
        }catch (Exception e) {//还有一些 根本临时想不到的异常
            System.out.println("其他异常。。。");
        }
    }

throw 一般和 自定义异常 一起使用

throw: 声明异常

jdk中自带了很多类型的异常,但如果 这些内置的异常 仍然不能满足项目的需求,那么就需要创建 自定义异常。

如何编写自定义异常:

1类,继承Exception,调用super(“异常信息”)

package ex;

//public class MyException  extends  Throwable{
//public class MyException  extends  NullPointerException{
public class MyException  extends  Exception{//推荐
    public MyException(String message){//异常信息
        super(message);
    }
}


2.使用throw 声明一个自定义异常、并且进行try catch或throws

package ex;
public class Demo03 {

    public static void main(String[] args)   {
        int age = 188 ;
        //约定,年龄不能大于120
        if(age<= 0 || age>120){
            try {
                //手工声明一个异常
                throw new MyException("年龄不能大于120");
            }catch ( MyException e ){
                e.printStackTrace();
                System.out.println(e.getMessage());
            }
        }
    }
}

在开发的过程中,要逐步积累异常,有助于提高我们的 排查bug的能力。

Java注解

类:要给一个类增强一些功能? 继承、实现一个接口 ,还可以使用注解

class A extends B

class A implements C

可以通过使用注解 增强类、方法、属性的功能。

内置注解

@Override :可以确保 重写的方法  的确存在于父类/接口中,可以有效的避免 单词拼错等情况。
@Deprecated:给用于提示,该方法由于安全、性能问题等 已经不推荐使用了。此外,在版本升级时,如果要计划删除一些方法,也通常会在前一个版本中 将该方法加上@Deprecated,然后再在后续版本中删除。

@SuppressWarnings(value="unchecked")//压制警告 (虽然可以使用SuppressWarnings压制警告,但不建议使用。)//忽略对泛型等的检查操作。value值:unchecked ,deprecation(忽略一些过期的API) ,unused (是否未被使用)  ,fallthrough(swtich 是否一致往下执行,而没有break);path(忽略 对类路径不存在的检查)  ,serialVersionUID(忽略 一个类可以序列化、但却没有序列化的 警告),all



package annotaion;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

class Father {
    public void eat() {
        System.out.println("father eat...");
    }
}

class Son extends Father{
    @Override
    public void eat() {
        System.out.println("son eat...");
    }

    @Deprecated
    public void test(){

    }
}


//压制警告 (虽然可以使用SuppressWarnings压制警告,但不建议使用。)
@SuppressWarnings(value="all")//忽略对泛型等的检查操作
public class Demo01 {
    public static void main(String[] args) {
        int a = 1 ;

        Father f = new Son();
        f.eat();

        new Thread().stop();
        new Son().test();
        List list = new ArrayList();
    }
}

自定义注解

public @interface MyAnnotation {
}

元注解

元数据:修饰数据的数据

元注解:修饰注解的注解, @Target、@Retention、@Document、@Inherited

@Target:限制注解 可以使用的位置

限制注解 能够使用哪些元素上(属性、方法、类);如果一个注解没有@Target描述,则该注解 可以修饰任何类型的元素; 如果有@Target修饰,该注解就只能用于 被@Target修饰的地方

哪些位置? ElementType.枚举

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang.annotation;

/**
 * The constants of this enumerated type provide a simple classification of the
 * syntactic locations where annotations may appear in a Java program. These
 * constants are used in {@link Target java.lang.annotation.Target}
 * meta-annotations to specify where it is legal to write annotations of a
 * given type.
 *
 * <p>The syntactic locations where annotations may appear are split into
 * <em>declaration contexts</em> , where annotations apply to declarations, and
 * <em>type contexts</em> , where annotations apply to types used in
 * declarations and expressions.
 *
 * <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
 * #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
 * {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
 * to the declaration contexts in JLS 9.6.4.1.
 *
 * <p>For example, an annotation whose type is meta-annotated with
 * {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
 * field declaration.
 *
 * <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
 * 4.11, as well as to two declaration contexts: type declarations (including
 * annotation type declarations) and type parameter declarations.
 *
 * <p>For example, an annotation whose type is meta-annotated with
 * {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
 * (or within the type of the field, if it is a nested, parameterized, or array
 * type), and may also appear as a modifier for, say, a class declaration.
 *
 * <p>The {@code TYPE_USE} constant includes type declarations and type
 * parameter declarations as a convenience for designers of type checkers which
 * give semantics to annotation types. For example, if the annotation type
 * {@code NonNull} is meta-annotated with
 * {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
 * {@code class C {...}} could be treated by a type checker as indicating that
 * all variables of class {@code C} are non-null, while still allowing
 * variables of other classes to be non-null or not non-null based on whether
 * {@code @NonNull} appears at the variable's declaration.
 *
 * @author  Joshua Bloch
 * @since 1.5
 * @jls 9.6.4.1 @Target
 * @jls 4.1 The Kinds of Types and Values
 */
public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,

    /** Field declaration (includes enum constants) */
    FIELD,

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

自定义一个注解

//修饰该注解 只能在 属性、方法上使用
@Target(value= {ElementType.FIELD   ,ElementType.METHOD} )
public @interface MyAnnotation {
    /*
        用定义方法的形式,定义一个属性 value
        方法的名字,就是属性的名字;方法的返回值,就是属性的类型
     */
    String value()  default "张三";//String value = "张三" ;
    int age() default  22 ;
}

使用

public class TestMyAnnotation {
    @MyAnnotation(value="李四",age=33)
    public void test(){

    }
}

@Retention:限制注解的生命周期

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
       jvm直接将该注解丢弃 ,写了跟没写一样
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
       .java -> .class
       程序在编译时 会使用注解,在运行时不会使用
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
       程序在编译以及运行时,都会使用注解
     */
    RUNTIME
}

自定义注解如何使用? 结合反射使用。

注解+反射 什么时候会真正使用? 开发框架 ,instanceof判断类型

使用注解案例

package annotaion;

import java.lang.annotation.Annotation;

public class TestMyAnnotation {


    @MyAnnotation(value="李四",age=33)
    @Deprecated
    public static void test() throws  Exception{

        Annotation[] annotations = Class.forName("annotaion.TestMyAnnotation").getMethod("test").getAnnotations();
        for(Annotation a :annotations){
            if(a  instanceof  MyAnnotation ){//@MyAnnotation
                System.out.println(    ((MyAnnotation)a)  .value()     );
                System.out.println(    ((MyAnnotation)a)  .age()     );
            }else{//@Deprecated
                System.out.println(        "@Deprecated"     );
            }
        }
    }

    @SuppressWarnings("all")
    public static void main(String[] args) throws  Exception {
        test() ;
    }
}

运行结果

李四
33
@Deprecated

@Document:

​ javadoc:java帮助文档。 ABC.java -> 帮助文档

​ 默认情况下,javadoc不包含 注解的解释;如果现在javadoc文档中 也包含对注解的说明,则需要使用 @Document标注

例如,一下MyAnnotation注解,会在生成javadoc时,被显示在文档中

@Documented
public @interface MyAnnotation {

}

@Inherited:继承

@Inherited
public @interface MyAnnotation {
}

@MyAnnotation
public  class A{}

public  class B extends A{}//默认情况下,B不会继承A中的注解;如果要想继承,则需要加@Inherited

IO

输入流InputStream 输出流OutputStream

java.io.*

文件:硬盘上的文件 txt docx 电影 图片

本章的核心:通过 IO 来操作文件。

File

import java.io.File;
import java.io.IOException;

public class Demo01 {
    public static void main(String[] args) {
        //file:文件\目录\不存在的文件或目录
//        File file = new File("d:/abc.txt");
        File file = new File("hello.txt");
        System.out.println("相对路径:"+file.getPath() );
        System.out.println("绝对路径:"+file.getAbsolutePath());
        System.out.println("文件名称:"+file.getName() );
        System.out.println("文件大小:"+file.length() );//单位字节
        
        System.out.println(      file.isFile()==true? "文件":"非文件" );
        System.out.println(      file.isDirectory()==true? "目录":"非目录" );
        boolean flag = file.exists();
        try {
            if(flag) {
//                file.delete();
                //彻底删除(不过回收站)
                System.out.println("删除成功");
            }
            else {
                file.createNewFile();
                System.out.println("创建成功");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

相对路径/绝对路径:

- 如果FIle("绝对路径"):getPath() 和getAbsolutePath()的结果一致,打印的都是绝对路径
- 如果FIle("相对路径"):getPath() 打印相对路径;getAbsolutePath()打印绝对路径

1575598723404

流:是一种FIFO(先进先出)的数据结构

分类:

1576199955793

说明:1.字节流就是 将内容转为了字节形式进行传输, 1 字节 ->8二进制 ,二进制可以传输任何类型的数据,因此字节流也可以传输任何类型的数据。

2.字节流是8位通用字节流( 1 字节 ->8二进制 ) (字节流可以处理任何类型,处理文本文件以外的其他文件) ;字符流是16位的unicode字符流 (只用于处理字符,处理文本文件)

3.在while循环中 分批次传输时,字节流使用的缓冲区是 byte[],字符流使用的缓冲区是 char[]

  • 其他流

文件复制

1575601580784

输入

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class InputStreamDemo {
    public static void main(String[] args) {
        InputStream in = null ;
        try {
            in =   new FileInputStream(new File("d:/abc.txt"));
//            InputStream in = new FileInputStream("d://abc.txt");
            System.out.println(in.available());//file.length()
            byte[] buf = new byte[in.available()] ;
            in.read(buf);//将文件abc.txt内容读取到buf中
            //buf :byte[] ->String
            System.out.println(new String(buf));
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

输出

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamDemo {
    public static void main(String[] args) {
        OutputStream out = null ;
        try {
             out = new FileOutputStream("d:/xyz.txt") ;
             out.write("helloworld123".getBytes()); //内存->xyz.txt
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

文件复制

import java.io.*;

public class FileCopy {
    //abc.txt->内存->xyz.txt
    public static void main(String[] args) {
        InputStream in = null ;
        OutputStream out = null ;
        try {
            //abc.txt->内存
             in = new FileInputStream("d:/abc.txt") ;
             out = new FileOutputStream("d:/xyz.txt") ;
            //开辟10字节的内存
            byte []buf = new byte[10] ;
            int len = -1 ;
            ;
        while(   (len= in.read(buf)) != -1){//in ->buf
            out.write(buf,0,len);//buf->out
        }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(out !=null)out.close();
                if(in !=null) in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

1575602327025

使用字符流进行文件的复制

1576201905525

import java.io.*;

public class FileCopyCharactor {
    public static void main(String[] args) {
        //文件->内存(Reader)
        Reader reader = null ;
        Writer writer = null ;
        try {
             reader = new FileReader("d:/个人介绍.txt");
             writer = new FileWriter("d:/个人完整介绍.txt") ;

            char[] buf = new char[4];
            StringBuffer sb = new StringBuffer() ;

            int len = -1 ;
            while((len =reader.read(  buf)) != -1 ){
                // str += buf ;
                sb.append(buf,0,len) ;//将每次读取到的 4个字符 拼接起来
            }
            System.out.println(sb);
            //在内存中 替换占位符
            String content = sb.toString() ;
            content= content.replace("{name}","哈哈")
                    .replace("{enterprise}","xx")
                    .replace("{weixin}","xx");

            //将替换后的内容 输出到文件   ,内存 ->文件(Writer)
            writer.write(content);
            System.out.println("成功...");

//            writer.flush(); 将管道中的数据 刷出到 文件中

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
               if(writer!=null) writer.close();
                if(reader!=null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

自带缓冲区的字符流:缓冲区大小,一行

BufferedReader /BufferedWriter

import java.io.*;

public class FileCopyCharactorBuffered {
    public static void main(String[] args) {
        //文件->内存(Reader)
        Reader reader = null ;
        Writer writer = null ;

        BufferedReader br = null ;
                BufferedWriter bw = null ;

        try {
             reader = new FileReader("d:/个人介绍.txt");
             writer = new FileWriter("d:/个人完整介绍2.txt") ;


             br = new BufferedReader( reader) ;
             bw = new BufferedWriter( writer);

            StringBuffer sb = new StringBuffer() ;

            String line = null ;

            while( (line= br.readLine()) != null   ){
                sb.append(line) ;
            }

            System.out.println(sb);

            //在内存中 替换占位符
            String content = sb.toString() ;
            content= content.replace("{name}","哈哈")
                    .replace("{enterprise}","xx")
                    .replace("{weixin}","xx");

            //将替换后的内容 输出到文件   ,内存 ->文件(Writer)


            bw.write(content);
            System.out.println("成功...");

//            writer.flush(); 将管道中的数据 刷出到 文件中

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            /*
            *    1先关出,再关入
            *    2从外往内关  br外 = new BufferedReader( reader内) ;
            * */
            try {
               if(bw != null) bw.close();
              if(br!=null )  br.close();
               if(writer!=null) writer.close();
                if(reader!=null) reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

带缓冲区的字符流 对象 = new 带缓冲区的字符流(字符流) ;

二进制流

DataInputStream/DataOutputStream

二进制流 对象 = new 二进制流(字节流) ;

import java.io.*;
//二进制流
public class FileCopyData {
    //abc.txt->内存->xyz.txt
    public static void main(String[] args) {
        InputStream in = null ;
        OutputStream out = null ;
        InputStream dataInput = null ;
        OutputStream dataOutput = null ;
        try {
            //abc.txt->内存
             in = new FileInputStream("d://xx.JPG") ;

              dataInput = new DataInputStream(in) ;//字节流->二进制流

             out = new FileOutputStream("e:/xx.jpg") ;
              dataOutput = new DataOutputStream(out );//字节流->二进制流

            //开辟10字节的内存
            byte []buf = new byte[10] ;
            int len = -1 ;

        while(   (len= dataInput.read(buf)) != -1){//in ->buf
            dataOutput.write(buf,0,len);//buf->out
        }
            System.out.println("成功");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(dataOutput !=null) dataOutput.close();
                if(dataInput !=null) dataInput.close();
                if(out !=null)out.close();
                if(in !=null) in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }


        }
    }
}

装饰模式:IO设计的核心思想

在不影响原有对象的前提下,无侵入的给一个对象增一些额外的功能。(套娃!!)

new InputStreamReader( new FileInputStream(new File(“d:/abc.txt”)) ) ;

装饰模式

1576461139194

装饰者:需要持有主题(被装饰者)的一个引用。

1576462471474

装饰主题接口

package decorator;
public interface Phone {
    void call();//基础功能
}

装饰主题的基础功能

package decorator;
public class ConcretePhone implements  Phone {
    @Override
    public void call() {
        System.out.println("打电话....");
    }
}

装饰者抽象类

package decorator;
public abstract class SmartPhone  implements  Phone{
   private  Phone phone  ;//装饰者 持有 主题的一个引用

   public SmartPhone(){
   }

   public SmartPhone(Phone phone){
      this.phone = phone ;
   }

   //装饰者 包含 原主题已有的功能
   @Override
   public void call() {
      phone.call();
   }
}

具体装饰者1

package decorator;
public class ConcreteSmarPhone1  extends  SmartPhone{
    public ConcreteSmarPhone1(Phone phone){
        super(phone) ;
    }
    public void call(){
        super.call();
        //额外的新功能
        changeColor();
    }
    public void changeColor(){
        System.out.println("智能变色...");
    }
}

具体装饰者2

package decorator;
public class ConcreteSmarPhone2 extends  SmartPhone{
    public ConcreteSmarPhone2(Phone phone){
        super(phone) ;
    }
    public void call(){
        super.call();
        //额外的新功能
        changeSize();
    }
    public void changeSize(){
        System.out.println("智能改变尺寸...");
    }
}

测试类

package decorator;
public class Test {
    public static void main(String[] args) {
        ConcretePhone phone = new ConcretePhone();
        //主题(被装饰者)
        phone.call();
        System.out.println("---");

ConcreteSmarPhone1 phone1 = new ConcreteSmarPhone1( phone ) ;
        phone1.call();

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

        ConcreteSmarPhone2 phone2 = new ConcreteSmarPhone2( phone ) ;
        phone2.call();


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

       SmartPhone smart =  new ConcreteSmarPhone2(new ConcreteSmarPhone1( phone )) ;
        smart.call();
    }
}

网络编程

1576568236682

ip地址:网络上的 唯一标识。

ip的组成:32位,由4个8位二进制组成 (每一位最大值是255,每一位十进制标识 0-255)

‭11000000‬.‭10101000‬.‭00000001‬.‭01100100‬ 二进制不容易被记忆,改成十进制:192.168.1.100

192.168.300.101 错 (每一位最大值是255,每一位十进制标识 0-255)

协议: 为了让网络中不同计算机之间能够相互通信而建立的规则、标准、约定。本课程使用的TCP、UDP

  • TCP协议:面向连接的、可靠的(不丢失、不重复、有序)、基于字节流的传输通信协议。传输速度相对慢。
  • UDP协议:无连接的协议。在传输数据之前,客户端和服务端之间不建立和维护连接。提供不可靠的数据传输。传输速度相对快。

socket(套接字):基于TCP协议的网络通信,可以提供双向安全连接的网络通信。socket需要借助于数据流(字节流)来完成数据的传递工作

1576569161833

可见:发送数据,OutputStream

接受数据,InputStream

本机默认ip:localhost 127.0.0.1

案例:

模板

1.客户端建立连接 ;服务端准备服务(ip[默认本机]:端口)

2.通过socket生成inputstream/outputstream(准备发送数据)

3.使用inputstream/outputstream进行发送、接受数据

4.关闭inputstream/outputstream、socket

1576571307737

服务端(ServerSocket)

package demo01;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer {

    public static void main(String[] args) {
//        1.准备服务.ip:默认本机127.0.0.1,端口8888
        ServerSocket serverSocket = null ;
        Socket socket = null ;
        InputStream in = null ;
        BufferedReader reader = null ;
        OutputStream out = null ;
        try {
             serverSocket = new ServerSocket(8888) ;
            System.out.println("服务器启动");
            //准备完毕,可以监听客户端请求
             socket = serverSocket.accept();//一直阻塞,直到有客户端连接
            System.out.println("服务端检测到客户端连接成功!");
            

            //  2.通过socket生成inputstream/outputstream(准备发送数据)
             //3.使用inputstream/outputstream进行发送、接受数据
             in = socket.getInputStream();
            //带缓冲区的字符流(字节流-转换流-字符流)
             reader = new BufferedReader(new InputStreamReader(in));
            String info = null ;
            while(  (info=reader.readLine()) != null       ){
                System.out.println("I am server,接受到客户端信息是:" + info);
            }

            socket.shutdownInput();



            //服务端做出反馈
             out = socket.getOutputStream();
            out.write("welcome  client....".getBytes());

        socket.shutdownOutput();




        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
                // 4.关闭inputstream/outputstream、socket
               if(reader !=null) reader.close();
                if(out !=null) out.close();
                if(in !=null) in.close();
                if(socket !=null) socket.close();
                if(serverSocket !=null) serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

客户端(Socket)

package demo01;

import java.io.*;
import java.net.Socket;
public class MyClient {

    public static void main(String[] args) {
        Socket socket = null ;
        OutputStream out = null ;
        InputStream in = null ;
        BufferedReader reader = null ;
        try {
             socket = new Socket("127.0.0.1",8888);
            System.out.println("客户端链接成功!");
             out = socket.getOutputStream();
            out.write( "hello server".getBytes()  );
            socket.shutdownOutput();

            //接收服务端的反馈
             in = socket.getInputStream();
             reader = new BufferedReader(new InputStreamReader(in));
            String info = null;
            while(   (info=reader.readLine()) != null ){
                System.out.println("I am client,接收到的服务端消息:"+info);
            }

            socket.shutdownInput();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
               if(out!=null) out.close();
                if(in!=null) in.close();
                if(socket!=null) socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

对象流

网络传递一个对象: 1.将要传递的对象序列化 2.使用对象流(ObjectInputStream/ObjectOutputStream)

对象

1576632938710

package demo02;

import java.io.Serializable;
public class Student implements Serializable {//将要传递的对象序列化
    private int sid ;
    private String sname ;
    private int age ;

    public Student(){

    }
    public Student(int sid, String sname, int age) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", age=" + age +
                '}';
    }
}

服务端

package demo02;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer {
    public static void main(String[] args) {
        ServerSocket serverScoket = null ;
        Socket socket = null ;
        InputStream in = null ;
        ObjectInputStream ois = null ;
        try {
             serverScoket = new ServerSocket( 8888) ;
             socket = serverScoket.accept();

            //接受客户端发来的对象
             in = socket.getInputStream();
            //对象流
             ois = new ObjectInputStream(in);
            try {
                Student student = (Student)ois.readObject();//读取对象
                System.out.println(student);
                socket.shutdownInput();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
               if(ois!=null) ois.close();
                if(in!=null)   in.close();
                if(socket!=null)  socket.close();
                if(serverScoket!=null)  serverScoket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }

    }
}

客户端

package demo02;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class MyClient {
    public static void main(String[] args) {
        Socket socket = null ;
        OutputStream out = null ;
        ObjectOutputStream oos = null ;
        try {
             socket = new Socket("localhost",8888) ;

            Student student = new Student(1001,"zs",23);

             out = socket.getOutputStream();
            //将OutputStream转为对象流
             oos = new ObjectOutputStream(out) ;
            oos.writeObject( student );//发送对象
            socket.shutdownOutput();



        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            try {
                if(oos!=null) oos.close();
                if(out!=null)   out.close();
                if(socket!=null)  socket.close();
            }catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

多客户端问题

1576637353440

思路:客户端代码不变;服务端:每当有一个客户端,就开启一个新线程去处理(每个线程专门处理一个客户端对象)。

1576638316325

服务端线程

package demo03;

import java.io.*;
import java.net.Socket;
public class ServerThread  extends  Thread{

    Socket socket  ;
    public ServerThread(Socket socket){
      this.socket = socket;
    }

    @Override
    public void run() {
        InputStream in =null ;
        ObjectInputStream ois  = null ;
        OutputStream out = null ;
        try {
            //接受客户端数据(客户端—>服务端)
             in = socket.getInputStream();
             ois  = new ObjectInputStream(in) ;
            Student student =  (Student)ois.readObject() ;
            System.out.println(student);
            socket.shutdownInput();

            //给客户端反馈(服务端->客户端)
             out = socket.getOutputStream();
            out.write("已收到....".getBytes());

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        finally {
            try {
                out.close();
                ois.close();
                in.close();
                socket.close(); ;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

服务端

package demo03;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888) ;

            while(true) {
                Socket socket = serverSocket.accept();//阻塞,接受客户端请求
                ServerThread serverThread = new ServerThread(socket) ;
                serverThread.start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

客户端

package demo03;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
public class MyClient {
    public static void main(String[] args) {
        Socket socket = null ;
        OutputStream out = null ;
        ObjectOutputStream oos = null ;
        try {
             socket = new Socket("localhost",8888) ;

            Student student = new Student(1001,"zs",23);

             out = socket.getOutputStream();
            //将OutputStream转为对象流
             oos = new ObjectOutputStream(out) ;
            oos.writeObject( student );//发送对象
            socket.shutdownOutput();

            //接受服务端的反馈
            InputStream in = socket.getInputStream();
            byte[] buf = new byte[100] ;
            in.read(buf) ;
            System.out.println("接收到的服务端反馈:" + new String(buf)      );


        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            try {
                if(oos!=null) oos.close();
                if(out!=null)   out.close();
                if(socket!=null)  socket.close();
            }catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
}

UDP的实现

DatagramPacket(数据):封装了数据报的数据、数据长度、目标地址和目标端口

DatagramScoket(收发器):接收和发送DatagramPacket中封装好的数据

接收方

package demo04;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class Receive {

    public static void main(String[] args) {
        DatagramSocket ds = null ;
        byte[] data = new byte[64] ;
        //准备接收数据的对象
        DatagramPacket dp = new DatagramPacket(data , data.length) ;
        //接收数据
        try {
             ds = new DatagramSocket(9999  );
            ds.receive( dp );

            //显示接收到的数据
            String receiveData  = new String(dp.getData(), 0, data.length);
            System.out.println("接收到的数据:"+receiveData);
            System.out.println("显示发送方的信息:" +  dp.getAddress().getHostAddress() );




        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            ds.close();
        }

    }
}

发送方

package demo04;

import java.io.IOException;
import java.net.*;
public class Send {
    public static void main(String[] args) {
        DatagramSocket ds = null ;
        //创建一个InetAddress对象
        InetAddress ip = null ;
        try {
             ip = InetAddress.getByName("127.0.0.1");


        String msg = "helloserver.." ;
        DatagramPacket dp = new DatagramPacket(msg.getBytes(),msg.length(),ip,9999) ;
         ds = new DatagramSocket() ;
        ds.send(  dp );
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            ds.close();
        }
    }
}

总结:发送数据使用的工具

tcp:字节流

udp:DatagramScoket

网络编程中一些工具类

InetAddress

   public static void main(String[] args) {
        InetAddress host = null;
        try {
            //本机
            host = InetAddress.getLocalHost();
            System.out.println(host);
            //网络中任意一台
            InetAddress host163 = InetAddress.getByName("www.163.com");
            System.out.println(host163);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

    }

1576720208036

URL/URLConnection

1576720826201

以下示例,注意点:输入流 url-内存; 输出流:内存-本地

package demo05;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class URLDemo {
    public static void main(String[] args) {
        InputStream in = null ;
        URLConnection urlConnection = null;
        OutputStream out = null ;
        try {
            URL url = new URL("https://www.163.com/") ;
             urlConnection = url.openConnection();
             in = urlConnection.getInputStream();

             out = new FileOutputStream("d:\\163.txt");
            byte[] buf = new byte[64] ;
            int len = -1 ;
            while(  (len = in.read(buf)) != -1 ){
                out.write(buf,0,len);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }finally{
            try {
               if(out!=null) out.close();
                if(in!=null)  in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }



        }

    }
}