Java SE

环境

  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 array = new ArrayList(); 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 c = new ArrayList();
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 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 map = new HashMap<>();
        map.put("23号", "James");
        map.put("24号", "Kobe");
        map.put("35号", "Durant");

        // 方式1:获取所有的键 再获取值
        Set keys = map.keySet();:
        // 遍历键 获取值
        for (String key : keys) {
            System.out.println(key + ":" + map.get(key));
        }

        // 方式二,通过内部类 Entry 获取每对键和值
        Set> entries = map.entrySet();
        for (Map.Entry 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 SE_第1张图片
    Java中的内存分配
  2. 成员变量和局部变量区别
  • 在类中位置不同,成员变量在类中方法外;局部变量在方法中或者方法声明上(形式参数)
  • 在内存中位置不同,成员变量在对象中,也就是堆内存中;局部变量在栈内存中
  • 生命周期不同,成员变量随着对象存在;局部变量随着方法的调用存在
  • 初始化,成员变量无需初始化化,有默认值;局部变量要使用必须初始化

你可能感兴趣的:(Java SE)