Java SE

作者: hgzzz | 来源:发表于2019-04-17 13:33 被阅读0次

    环境

    1. 安装JDK(Java Development Kit),Java 语言的软件开发工具包
    2. 配置电脑的PATH环境变量
    3. 安装IDEA

    基础语法

    1. 注释:单行使用//单行注释,多行使用/*多行注释*/
    2. 关键字:全部小写,代码编辑器有颜色标记
    3. 常量
    • 字符串常量 "Hello World"
    • 整数常量 12
    • 小数常量 12.23
    • 字符常量 a
    • 布尔常量 true false
    • 空常量 null
    1. 变量:内存中的一小块区域,在执行过程中,其值在一定的范围内改变
    • 变量要限定数据类型
    • 需要一个变量名
    • 初始化值,未赋予初始值的变量不能直接使用
    • 变量只在它的作用域内有效(块级作用域)
    1. 数据类型:Java是强类型语言,针对每一种数据都有明确的数据类型
    • 基本数据类型
      a. 整数:byte(1字节),short(2字节),int(4字节),long(8字节)
      b. 浮点数(float(4字节),double(8字节))
      c. 字符(char(2字节)
      d. 布尔(boolean(1字节)
    • 整数默认是int型,浮点数默认是double类型
    • 定义浮点类型变量时,在变量后面加上类型:12.31F 10000000000L
    1. 类型转换
    • 隐式转换:参与运算时转为存储空间更大的类型,防止损失精度
    • 强制类型转换:目标类型 变量名 = (目标类型)(数据)
    1. 标识符:给变量、包、类、方法取的名称
    • 组成:数字、大小写字母、下划线、$
    • 注意:不能以数字开头、不能是关键字
    • 命名规则:见名知意
      a. 包(文件夹,对类进行管理):全部小写,多级包用.隔开
      b. 类:大驼峰命名(HelloWorld)
      c. 方法和变量:小驼峰(helloWorld)
    1. 运算符
    • 算数运算:+, -, *, /, %, ++, --
      a. 整数相除得到整数,要想得小数,必须有浮点数参与运算
      b. 字符参与加法运算,使用的是字符的ASCII
      c. 字符串做加法运算,其实是在做字符串拼接
    • 赋值运算符:=, +=, -=, %=
      a. 扩展的赋值运算符隐含了强制类型转换
    • 关系运算符:==, !=, >, >=, <, <=
    • 逻辑运算符:&(与),|(或),!(非),^(异或),&&(双与),||(双或)
      a. &&在左边表达式为false时,右边不执行,而&则会去执行右边,|||也是同样的区别
    • 三元运算符:关系表达式?表达式1:表达式2
    1. 获取键盘输入
    • 使用JDK 的Scanner
    package com.syntax;
    
    // 导包
    import java.util.Scanner;
    
    public class ScannerDemo {
        public static void main(String[] args) {
            // 创建键盘输入对象
            Scanner sc = new Scanner(System.in);
            // 接收数据
            System.out.println("请输入一个数据:");
            int i = sc.nextInt();
            System.out.println("你输入了:" + i);
        }
    }
    
    1. 流程控制
    • 顺序结构:从上往下执行
    • 选择结构(if语句,switch语句)
    • 循环结构语句(for语句,while语句,do...while语句)
      a.for语句中初始化值只在循环体里面有效,而while语句的初始化值在循环体外,所以在外面也可以访问初始化值
      b. do...while会先执行一次循环体,再做条件判断
      c. break,结束整个循环
      d. continue,结束本次循环,继续下一次循环
    1. Random,用于产生随机数
    package com.syntax;
    
    import java.util.Random;
    
    public class RandomDemo {
        public static void main(String[] args) {
            Random random = new Random();
            int number = random.nextInt(10); // [0,10)
            System.out.print(number);
        }
    }
    
    1. 数组:存储多个同一数据类型元素的容器,可以存储基本数据类型或者引用数据类型,长度定义好了则无法改变。
    • 声明方式
      a. int[] arr;:定义一个数组,名为arr(推荐使用)
      b. int arr[];:定义arr变量,是一个数组
    • 数组初始化
      a. 动态初始化,只给出长度,系统决定值 int[] arr = new int[10];
      b. 静态初始化,只给出值,系统决定长度 int[] arr = new int[]{1,2,3}int[] arr = {1,2,3}
    • 二维数组 :元素为一维数组的数组
      a. 声明:int[][] arr;int arr[][];int[] arr[];,推荐使用第一种
      b. 初始化:动态:int[][] arr = new int[2][3],静态:int[][] arr = {{1,2},{3,4}}
      c. 遍历二维数组
    package com.syntax;
    
    public class ArrayDemo {
        public static void main(String[] args) {
            int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
            for (int x = 0; x < arr.length; x++) {
                for (int y = 0; y < arr[x].length; y++) {
                    System.out.println(arr[x][y]);
                }
            }
        }
    }
    
    
    • 对象数组,用来存储对象的数组,长度固定
      Student[] stus = new Student[3];
    1. 方法:完成特定功能的代码块
    • 格式
    权限修饰符 返回值类型 方法名 (参数类型 参数名1,参数类型 参数名2){
        方法体
        return 返回值
    }
    权限修饰符:public default(不加修饰符,当前包下可用) private(只能在当前类访问) protected(子类对象)
    返回值类型: 限定返回值的类型,如果没有返回值为void
    参数类型:限定实参的数据类型
    参数名:形参名称
    
    • 写方法的方式
      a. 明确返回值类型
      b. 参数列表
    // 定义一个求和函数
    package com.syntax;
    
    public class MethodDemo {
        public static void main(String[] args) {
            System.out.println(sum(1,2));
        }
        public static int sum(int a, int b) {
            int c = a + b;
            return c;
        }
    }
    
    • 方法的重载
      a. 在同一个类中,方法名相同
      b. 参数不同,可以是参数个数不同,或者是参数对应的数据类型不同
      c. 和返回值无关
    • 方法的实参和形参
      a. 形参是定义函数时给的参数名称,实参是函数调用时传入的参数值
      b. 对于基本数据类型,形参改变并不会改变实参(函数在调用时,实参会赋值给形参),而引用数据类型则会使得它们都发生改变,因为它们是指向同一块内存区域(堆)
    1. 代码块 用 {}括起来的代码
    • 局部代码块:写在方法中,用来控制变量的作用域
    • 构造代码块:直接写在类中,用来提取无参构造函数和有参构造函数共有的代码块,会在对象实例化时调用,相当于写在了构造函数中(执行会先于构造函数代码)
    • 静态代码块,也是直接写在类中,随着类的加载而加载,只执行一次,用来做类的一些初始化工作
    1. 修饰符
    • public(类,成员变量,成员方法,构造方法):公用
    • default(类,成员变量,成员方法,构造方法):当前包下可用
    • protected(成员变量,成员方法,构造方法):子类可用
    • private(成员变量,成员方法,构造方法):当前类可用(用来修饰构造方法表示该类不能被实例化)
    • abstract(类,成员方法):定义抽象类和抽象方法
    • static(成员变量,成员方法):可通过类名调用
    • final (类,成员变量,成员方法):不可继承,不可改变(重写)
    • 常见规则
      a. 类:开发时一个java文件中一般只写一个类。如果要写多个类,类名与文件名相同时必须用public修饰,其他类不能用 public修饰
      b. 成员变量:开发时都使用private修饰,然后加上对应的get set方法
      c. 成员方法:用public修饰
      d. 构造方法:用public修饰,如果不想让它创建对象,就用private

    面向对象

    1. 类与对象
    • 类:成员变量,成员方法
      a. 成员变量:写在类里面,不需要给定初始值
      b. 成员方法:无 static 关键字,通过对象调用
      c. 静态方法,有static关键字,可以使用类名直接调用,例如Math
      d. private关键字,修饰符,用来修饰成员变量或者成员方法,加该关键字表示只能在本类中访问,不能通过实例对象访问。加该关键字的成员变量可以为它添加对应的set、get方法来赋值,而不是直接访问赋值
      e. static关键字,用于修饰静态成员变量和静态成员方法,使之被所有对象共享。可以使用类名直接调用,在内存中和类一起加载在方法区中,(先于对象),所以静态方法只能调用静态方法和静态成员变量;非静态方法中可以调用非静态、静态成员变量和方法
      f. final关键字,可以修饰 类(不能被继承)、成员方法(不能被子类重写)、成员变量(必须初始化,可以显式初始化或者构造初始化,不可以修改,也就是常量)
      g. toString方法,存在于Object中,返回getClass().getName() + '@' + Integer.toHexString(hashCode())即类名加地址的十六进制值。当我们试图去打印一个对象时,会默认调用这个方法。所以我们一般会去重写这个方法用来测试。
    • 对象:属性,行为
      a.对象中的属性必须对应类中的成员变量,没有再类中定义的属性就无法使用
    • 构造方法:给对象数据进行初始化
      a. 方法名和类名相同
      b. 没有返回值和返回值类型,连 void都不能写 如
      c. 通过new关键字调用,创建对象时调用,如果我们没有编写构造方法,系统则会自动提供一个无参构造方法
      d. 构造方法也可以重载
    public Student(name,age) { // 构造方法
        this.name = name;
        this.age = age;
    }
    
    1. 封装
    • 概述:把成员变量隐藏在对象内部,外界无法操作和修改
    • 原则:把不需要对外提供的内容隐藏起来,提供公共方法来对其访问
    1. 继承 extends
    • 多个类有共有的成员变量和成员方法,我们将它抽取到同一个父类类中,然后其他类去继承这个父类,达到复用的效果
    • Java中只支持单一继承,但支持多层继承
    • 子类只能继承父类的非私有成员
    • super表示父类的引用
    • 方法的重写,在继承中,子类的方法和父类完全一样,子类重写了父类的方法,重写后想要调用父类的方法可以使用super关键字去调用
    • 构造方法的执行顺序,在子类初始化实例对象时,会执行子类的构造方法,子类的构造方法如果在第一行没有调用父类的构造方法(super())自身的其他构造方法(this()),系统会默认调用父类的无参构造方法,所以在子类实例化时一定会调用一次父类的构造方法,其目的是初始化父类的成员变量供子类使用
    • 缺点:增强了类与类之间的耦合性
    1. 抽象 abstract,用于修饰方法和类
    • 抽象类:abstract class ClassName{}
    • 抽象方法:不同类的方法是相似的,但是内容又不是完全一样,所以我们只抽取它的声明,没有具体的方法体,这种方法叫做抽象方法,只能存在于抽象类中public abstract void method();
    • 非抽象子类如果继承的父类是抽象类,一定要重写父类的抽象方法
    • 抽象类不能创建对象,但是有构造方法,用来初始化成员变量
    1. 接口:处理单一继承的局限性
    • 接口中只能写抽象方法,而且也不能实例化 定义接口:interface 接口名{}
    • 接口和类的关系是实现,可以多实现(一个类实现多个接口), class 类名 implement 接口名{},类中必须实现接口的所有抽象方法
    • 接口中的方法默认且只能使用public&abstract修饰符,建议默认写上修饰符
    • 默认使用public static final来修饰成员变量(也就是常量)
    • 接口和接口之间是继承关系,而且是多继承
    • 优点:一对多实现,打破继承的局限性;对外提供规则的方法和变量;降低耦合
    1. 多态:允许将子类类型的指针赋值给父类类型的指针
    • 前提:继承关系,方法重写,父类引用指向子类对象(Father f = new Son();
    • 成员特点
      a. 成员变量:编译时看左边,运行时也是看左边
      b. 成员方法:编译时看左边,运行时看右边(动态绑定,看具体的数据类型)
      c. 静态方法:编译时看左边,运行时看左边。因为使用变量去调用静态方法,相当于用变量的类名去调用。
      d. 缺点:无法直接访问子类特有成员,
      e. 优点:提高可维护性,提高可扩展性
    1. this关键字:代表所在类的对象引用,super:代码父类的引用
      a. 静态方法随着类加载而加载(优先于对象),所以静态方法中没有this
      b. 通过this()调用子类的构造函数,super()调用父类的构造函数
    2. 字符串对象
    • 存储
      a. 字符串对象存储在堆中,字符串内容存储在方法区的常量池中(方便字符串的重复使用)
    • 创建
      a. 直接赋值 String str = "hello",直接指向方法区的常量池中字符串内容
      b. 构造函数 String str = new String("hello") 先在堆中创建一个字符串对象,这个对象存储着方法区的常量池中字符串内容的地址
      c. 传入一个字符数组String str = new String(char[], start, end)从字符数组截取一段包装成字符串对象
      d. StringBuilder 可变的字符串序列,解决字符串拼接的内存浪费问题(String是不可变的,拼接是产生一个新的字符串)
      e. StringBuilder转成String使用toString()StringStringBuilder,使用构造方法 StringBuilder sb = new StringBuilder(str)
    1. 集合类,长度可变
    • 使用
      a. 使用前需要导包
      b. ArrayList<E> array = new ArrayList<E>(); E表示泛型(任意引用数据类型,表示你当前集合要存储的引用数据类型)
    • 操作
      a. 增加:add()
      b. 获取 :get(int Index)
      c. 删除:remove(Object obj) 返回Booleanremove(int Index) 返回obj
      d. 修改:set(int Index, Object obj)返回被修改的内容
      e. 长度:size()
    • package关键字生命,在代码第一行,用来分类管理java文件
    • 有多层结构(一个包下有另一个包),不同包下的文件名可以重复
    • 相同包下的类可以直接访问;不同包下的类访问时要加上包名,或者使用关键字import将类导入
    1. 内部类:嵌套在其他类内部的类
    • 分类:成员内部类(类中),局部内部类(方法中),匿名内部类(实现接口或继承父类并立即创建一个对象,可以用来作为参数传递)
    • 内部类在编译时也会有单独的.class文件,但是前面会冠以外部类的类名和$符号。内部类是外部类的成员,所以内部类可以访问外部类的成员变量
    1. 包装类:封装了基本数据类型的类
    • byte(Byte) short(Short) int(Integer) long(Long) char(Character) float(Float) double(Double) boolean(Boolean)
    • Integer 可以用来转换intString类型的变量
      a. Integer i = new Integer("10"); 传入整数或者字符串,会被转换成整数
      b. String => int 使用 int intValue()static int parseInt(String s)
      c. int => String 使用 String toString() 或 直接加空字符串
      d. 自动装箱、拆箱 Integer i = 10; Integer i2 = i + 1;

    IO流

    1. 概述
    • 处理设备之间的数据传输,如在文件中读取数据,或者将数据存储到文件中
    • 可以进行文件复制,文件上传,文件下载
    1. 分类
    • 字符输出流 FileWriter 写数据,字节输出流OutputStream
    // 1. 创建输出流对象
    FileWriter fw = new FileWriter("filePath", boolean append);
    // 2. 写入内容
    fw.write("hello");
    // 3. 刷新文件
    fw.flush();
    // 4. 释放资源
    fw.close();
    // 内部实现:创建一个文件,创建输出流对象,将该对象指向该文件
    
    • 字符输入流 FileReader 读数据,字节输入流InputStream
    // 1. 创建输入流对象
    FileReader fr = new FileReader("filePath");
    // 2. 读数据 read()  一次读一个字符,返回该字符的ASCII码 使用循环来读出文件所有内容
    int ch;
    while((ch = fr.read()) != -1) {
        System.out.print((char)ch);
    }
    // 或者用一次读一个字符数组的方式读取, len 表示读出了几个字符(没读到则为-1)
    char[] chs = new char[1024];
    int len;
    while((len = fr.read(chs)) != -1) {
        System.out.print(new String(chs, 0, len));
    }
    // 3. 释放资源
    fr.close();
    
    • 字节流和字符流:字节流(一次读、写一个字节,适用于任何文件、比如图片、视频等文件的读写),字符流(一次读、写一个字符,适用于文本文件读写)
    • 对象操作流:使用对象输出流输出对象时,必须用对象输入流读取对象
      a. 对象输出流 ObjectOutputStream
      b. 对象输入流 ObjectInputStream
    public class ObjectOutputStreamDemo {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            // 对象输出流对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Src"));
            Student s1 = new Student("zs", 18);
            Student s2 = new Student("ls", 19);
            Student s3 = new Student("ww", 20);
    
            oos.writeObject(s1);
            oos.writeObject(s2);
            oos.writeObject(s3);
    
            // 对象输入流
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Src"));
            // 读取对象
    
            try {
                while (true) {
                    Object obj = ois.readObject();
                    System.out.println(obj);
                }
            }catch(EOFException e) {
                System.out.println("读完了");
            }
    
            ois.close();
            oos.close();
        }
    }
    
    1. 复制文件:也就是先读文件,再写文件
    2. 字符缓冲流
    • BufferedReaderBufferedWriter创建对象时传入一个FileReaderFileWriter,其他使用方式同IO流,缓冲流更为高效
    • 特殊功能
      a. bw.newLine();写一个换行,会根据系统决定是什么换行符
      b. br.readLine();读一行数据,不包括换行符,返回一个字符串数据
    1. File类: 文件和目录路径名的抽象表现形式,File 类的实例是不可变的
    • 构造方法
    File(File parent, String child)
    File(String pathname)
    File(String parent, String child)
    
    • 功能
      a. 创建功能 boolean createNewFile()
      b. 删除功能 boolean delete()
      c. 获取功能 File getAbsolutePath()等等
      d. 判断功能 boolean exists()
    // 获取所有java文件
     public static void getJavaFileName (File file) {
            File[] files = file.listFiles(); // 获取所有的文件和目录的 File 对象数组
            for (File f : files) { // 遍历得到每一个File对象
                if (f.isFile()) {
                    if (f.getName().endsWith(".java")) {
                        System.out.println(f);
                    }
                }
            }
        }
    

    集合体系结构

    • 最顶层是Collection
    • 集合的遍历,以ArrayList为例
    // 1. 创建集合对象
    ArrayList<String> c = new ArrayList<String>();
    c.add("hello");
    c.add("world");
    
    // 遍历方式1 Object toArray()
    Object[] objs = c.toArray  ();
    for (int i = 0; i < objs.length; i++) {
        String s = objs[i];
    }
    // 遍历方式2 Iterator 注意:迭代器是集合的一个副本,在操作过程中如果集合改变,则会抛出异常
    Iterator<String> it = c.iterator();
    while(it.hasNext()) {
        String s = it.next();
    }
    // 遍历方式2 增强for 在底层是和迭代器一样的,所以在使用时也不能去修改集合
    for (String s : c) {
        Systen.out.println(s);
    }
    
    • 常用集合类:
      1. ArrayList 底层结构是数组,查询快,增删慢
      2. LinkList 底层是链表,查询慢,增删快
    • Set集合:无序(存储和读取的顺序可能不同,无索引),无重复
    • Map双列集合 将键一对一(键唯一)映射到值的对象,一般使用
      hashMap
            // map 的遍历
            Map<String, String> map = new HashMap<>();
            map.put("23号", "James");
            map.put("24号", "Kobe");
            map.put("35号", "Durant");
    
            // 方式1:获取所有的键 再获取值
            Set<String> keys = map.keySet();:
            // 遍历键 获取值
            for (String key : keys) {
                System.out.println(key + ":" + map.get(key));
            }
    
            // 方式二,通过内部类 Entry 获取每对键和值
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key + ":" + value);
            }
    

    数据结构

    1. 数组
    • 查找方便,直接使用数组索引,但是数组长度和存储值的类型是不可变的,所以增删操作复杂
    1. 链表:存储当前地址、当前数据和下一个数据的地址
    • 查询慢,需要顺着链查找,但是增删快
    1. 栈和队列
    • 栈:先进后出
    • 队列:先进先出

    异常

    1. 异常:在代码编译或者运行时出现的错误,异常包括错误的类型、原因和位置
    2. 体系结构:Throwable是所以错误和异常的超类
    Throwable(最顶层)
      Error(不能处理的严重问题)
      Exception(可以处理的问题)
    
    1. 异常的处理
    • JVM:会把异常输出在控制台上,并终止程序的执行
    • 捕获处理 try...catch
    try {
    // 有可能出错的代码
    } catch (Exception e) {  
    // 代码出错时执行,处理异常
    } finally {
    // 一定会执行,用于释放资源、处理垃圾的收尾工作    
    }
    
    • 抛出异常: 当我们无法处理异常时(比如编译时异常),可以抛出异常,谁调用方法谁处理这个抛出的异常,使用关键字 throws
    1. 异常的分类
    • 运行时期异常:RuntimeException的子类,在编译时期可以不处理
    • 编译时期异常:Exception的子类,在编译时期处理
    1. 自定义异常
    public class ExceptionDemo {
        public static void main(String[] args) {
            checkScore(200);
        }
    
        public static void checkScore(int score) {
            if (score < 0 || score > 100) {
                throw new ScoreException("成绩错误");
            }
            System.out.println("正确的成绩");
        }
    }
    
    class ScoreException extends RuntimeException {
        public ScoreException() {
        }
    
        public ScoreException(String message) {
            super(message);
        }
    }
    

    多线程

    1. 概念
    • 进程:一个应用程序在内存中的执行区域
    • 线程: 进程中的执行控制单元,一个进程可以由一个线程(单线程:安全性高,效率低),或多个线程(多线程:效率高,存在安全问题)组成
    1. Thread
    • 使用
      a. 定义一个类继承Thread类并重写 run() 方法,在里面写这个写线程要做的事,然后创建线程对象再调用start()方法启动线程
      b. 定义一个类实现 Runable接口,实现run()方法,创建线程时传递一个该类的对象并启动线程
      c. 匿名内部类实现
    public class ThreadDemo {
        public static void main(String[] args) {
            // 使用匿名内部类 创建线程1
            Thread t1 = new Thread() {
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        System.out.println(this.getName() + "使用匿名内部类实现多线程");
                    }
                }
            };
    
    
            Thread t2 = new Thread() {
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        System.out.println(this.getName() + "使用匿名内部类实现多线程");
                    }
                }
            };
            t1.setName("Thread1");
            t2.setName("Thread2");
            t1.start();
            t2.start();
        }
    }
    
    1. 并发问题
      a. synchronized:同步(锁),可以修饰代码块和方法,被修饰的代码块和方法一旦被某个线程访问,则会被锁住,其他线程无法访问
    // 效率低
    synchronized(锁对象) { // 锁对象需要被所有的线程共享
    // 线程代码
    }
    
    // 同步方法
    public synchronized void method() {
    // 锁对象是this,静态方法锁对象是当前类的字节码对象
    }
    
    1. 线程的生命周期
      a. 新建 继承Thread或实现Runable接口
      b. 就绪:已经具备了执行能力,但是没有执行权利
      c. 阻塞、等待(wait() notify()),注意:阻塞状态需要回到就绪状态
      d. 运行
      e. 死亡

    网络编程

    1. Socket
    • 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket
    • 网络通信其实就是Socket之间的通信,数据在两个Socket间通过IO传输
    1. 使用UDP协议收发数据
    • 发送
    public class UDPSend {
        public static void main(String[] args) throws IOException {
            // 1. 创建Socket对象
            DatagramSocket ds = new DatagramSocket();
            // 2. 创建数据并打包 DatagramPacket(数据包类,需要数据byte[],目标IP和端口号)
            String s = "hello world";
            byte[] bys = s.getBytes(); // 数据
            int length = bys.length; // 要发送数据长度
            InetAddress address = InetAddress.getByName("DESKTOP-7MTQ9R3"); // 目标设备ip
            System.out.println(address);
            int port = 9999; // 目标设备端口
            DatagramPacket dp = new DatagramPacket(bys, length, address, port); // 数据报包
            // 3. 发送数据
            ds.send(dp);
    
            // 4. 释放资源
            ds.close();
        }
    }
    
    • 接收数据
    public class UDPReceive {
        public static void main(String[] args) throws IOException {
            // 1. 创建接收端Socket对象
            DatagramSocket ds = new DatagramSocket(9999);
            // 2. 接收数据
            byte[] bys = new byte[1024];
            DatagramPacket dp = new DatagramPacket(bys, bys.length); // 接收数据包的容器对象
            System.out.println(1);
            ds.receive(dp); // 阻塞 等待数据
            System.out.println(1);
            // 3. 解析数据
            InetAddress address = dp.getAddress();
            byte[] data = dp.getData(); // 获取接收到的数据
            int length = dp.getLength(); // 获取接收到的数据长度
            // 4. 输出数据
            System.out.println("sender=>" + address.getHostAddress());
            System.out.println(new String(data, 0, length));
            // 5. 释放资源
            ds.close();
        }
    }
    
    1. 使用TCP协议收发数据
    • 发送
    // 使用TCP协议发送数据(客户端)
    public class TCPSend {
        public static void main(String[] args) throws IOException {
            // 1. 创建发送端(客户端)Socket对象(创建连接)
            Socket s = new Socket(InetAddress.getByName("DESKTOP-7MTQ9R3"), 6666);
            // 2. 获取输出流对象
            OutputStream os = s.getOutputStream();
            // 3. 发送数据
            String str = "hello world";
            os.write(str.getBytes());
            // 4. 释放资源
            os.close();
        }
    }
    
    • 接收
    public class TCPReceive {
        public static void main(String[] args) throws IOException {
            // 1. 创建接收端(服务端)Socket对象
            ServerSocket ss = new ServerSocket(6666);
            // 2. 监听接收数据 阻塞 返回一个Socket
            Socket s = ss.accept();
            // 3. 获取输入流对象
            InputStream is = s.getInputStream();
            // 4. 获取数据
            InetAddress address = s.getInetAddress();
            byte[] bys = new byte[1024];
            int len; // 存储读到的数据个数
            len = is.read(bys);
            // 5. 输出数据
            System.out.println("sender=>" + address);
            System.out.println(new String(bys, 0, len));
            // 6. 释放资源
            is.close();
        }
    }
    

    扩展

    1. Java中的内存分配


      Java中的内存分配
    2. 成员变量和局部变量区别
    • 在类中位置不同,成员变量在类中方法外;局部变量在方法中或者方法声明上(形式参数)
    • 在内存中位置不同,成员变量在对象中,也就是堆内存中;局部变量在栈内存中
    • 生命周期不同,成员变量随着对象存在;局部变量随着方法的调用存在
    • 初始化,成员变量无需初始化化,有默认值;局部变量要使用必须初始化

    相关文章

      网友评论

          本文标题:Java SE

          本文链接:https://www.haomeiwen.com/subject/mxfrbqtx.html