环境
- 安装JDK(Java Development Kit),Java 语言的软件开发工具包
- 配置电脑的PATH环境变量
- 安装IDEA
基础语法
- 注释:单行使用
//单行注释
,多行使用/*多行注释*/
- 关键字:全部小写,代码编辑器有颜色标记
- 常量
- 字符串常量
"Hello World"
- 整数常量
12
- 小数常量
12.23
- 字符常量
a
- 布尔常量
true false
- 空常量
null
- 变量:内存中的一小块区域,在执行过程中,其值在一定的范围内改变
- 变量要限定数据类型
- 需要一个变量名
- 初始化值,未赋予初始值的变量不能直接使用
- 变量只在它的作用域内有效(块级作用域)
- 数据类型:
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
- 类型转换
- 隐式转换:参与运算时转为存储空间更大的类型,防止损失精度
- 强制类型转换:目标类型 变量名 = (目标类型)(数据)
- 标识符:给变量、包、类、方法取的名称
- 组成:数字、大小写字母、下划线、$
- 注意:不能以数字开头、不能是关键字
- 命名规则:见名知意
a. 包(文件夹,对类进行管理):全部小写,多级包用.
隔开
b. 类:大驼峰命名(HelloWorld)
c. 方法和变量:小驼峰(helloWorld)
- 运算符
- 算数运算:
+, -, *, /, %, ++, --
a. 整数相除得到整数,要想得小数,必须有浮点数参与运算
b. 字符参与加法运算,使用的是字符的ASCII
码
c. 字符串做加法运算,其实是在做字符串拼接 - 赋值运算符:
=, +=, -=, %=
a. 扩展的赋值运算符隐含了强制类型转换 - 关系运算符:
==, !=, >, >=, <, <=
- 逻辑运算符:
&(与),|(或),!(非),^(异或),&&(双与),||(双或)
a.&&
在左边表达式为false
时,右边不执行,而&
则会去执行右边,||
和|
也是同样的区别 - 三元运算符:
关系表达式?表达式1:表达式2
- 获取键盘输入
- 使用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);
}
}
- 流程控制
- 顺序结构:从上往下执行
- 选择结构(
if
语句,switch
语句) - 循环结构语句(
for
语句,while
语句,do...while
语句)
a.for
语句中初始化值只在循环体里面有效,而while
语句的初始化值在循环体外,所以在外面也可以访问初始化值
b.do...while
会先执行一次循环体,再做条件判断
c.break
,结束整个循环
d.continue
,结束本次循环,继续下一次循环
- 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);
}
}
- 数组:存储多个同一数据类型元素的容器,可以存储基本数据类型或者引用数据类型,长度定义好了则无法改变。
- 声明方式
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,参数类型 参数名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. 对于基本数据类型,形参改变并不会改变实参(函数在调用时,实参会赋值给形参),而引用数据类型则会使得它们都发生改变,因为它们是指向同一块内存区域(堆)
- 代码块 用
{}
括起来的代码
- 局部代码块:写在方法中,用来控制变量的作用域
- 构造代码块:直接写在类中,用来提取无参构造函数和有参构造函数共有的代码块,会在对象实例化时调用,相当于写在了构造函数中(执行会先于构造函数代码)
- 静态代码块,也是直接写在类中,随着类的加载而加载,只执行一次,用来做类的一些初始化工作
- 修饰符
-
public
(类,成员变量,成员方法,构造方法):公用 -
default
(类,成员变量,成员方法,构造方法):当前包下可用 -
protected
(成员变量,成员方法,构造方法):子类可用 -
private
(成员变量,成员方法,构造方法):当前类可用(用来修饰构造方法表示该类不能被实例化) -
abstract
(类,成员方法):定义抽象类和抽象方法 -
static
(成员变量,成员方法):可通过类名调用 -
final
(类,成员变量,成员方法):不可继承,不可改变(重写) - 常见规则
a. 类:开发时一个java文件中一般只写一个类。如果要写多个类,类名与文件名相同时必须用public
修饰,其他类不能用public
修饰
b. 成员变量:开发时都使用private
修饰,然后加上对应的get set
方法
c. 成员方法:用public
修饰
d. 构造方法:用public
修饰,如果不想让它创建对象,就用private
面向对象
- 类与对象
- 类:成员变量,成员方法
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;
}
- 封装
- 概述:把成员变量隐藏在对象内部,外界无法操作和修改
- 原则:把不需要对外提供的内容隐藏起来,提供公共方法来对其访问
- 继承
extends
- 多个类有共有的成员变量和成员方法,我们将它抽取到同一个父类类中,然后其他类去继承这个父类,达到复用的效果
- Java中只支持单一继承,但支持多层继承
- 子类只能继承父类的非私有成员
-
super
表示父类的引用 - 方法的重写,在继承中,子类的方法和父类完全一样,子类重写了父类的方法,重写后想要调用父类的方法可以使用
super
关键字去调用 - 构造方法的执行顺序,在子类初始化实例对象时,会执行子类的构造方法,子类的构造方法如果在第一行没有调用父类的构造方法(
super()
)或自身的其他构造方法(this()
),系统会默认调用父类的无参构造方法,所以在子类实例化时一定会调用一次父类的构造方法,其目的是初始化父类的成员变量供子类使用 - 缺点:增强了类与类之间的耦合性
- 抽象
abstract
,用于修饰方法和类
- 抽象类:
abstract class ClassName{}
- 抽象方法:不同类的方法是相似的,但是内容又不是完全一样,所以我们只抽取它的声明,没有具体的方法体,这种方法叫做抽象方法,只能存在于抽象类中
public abstract void method();
- 非抽象子类如果继承的父类是抽象类,一定要重写父类的抽象方法
- 抽象类不能创建对象,但是有构造方法,用来初始化成员变量
- 接口:处理单一继承的局限性
- 接口中只能写抽象方法,而且也不能实例化 定义接口:
interface 接口名{}
- 接口和类的关系是实现,可以多实现(一个类实现多个接口),
class 类名 implement 接口名{}
,类中必须实现接口的所有抽象方法 - 接口中的方法默认且只能使用
public&abstract
修饰符,建议默认写上修饰符 - 默认使用
public static final
来修饰成员变量(也就是常量) - 接口和接口之间是继承关系,而且是多继承
- 优点:一对多实现,打破继承的局限性;对外提供规则的方法和变量;降低耦合
- 多态:允许将子类类型的指针赋值给父类类型的指针
- 前提:继承关系,方法重写,父类引用指向子类对象(
Father f = new Son();
) - 成员特点
a. 成员变量:编译时看左边,运行时也是看左边
b. 成员方法:编译时看左边,运行时看右边(动态绑定,看具体的数据类型)
c. 静态方法:编译时看左边,运行时看左边。因为使用变量去调用静态方法,相当于用变量的类名去调用。
d. 缺点:无法直接访问子类特有成员,
e. 优点:提高可维护性,提高可扩展性
-
this
关键字:代表所在类的对象引用,super
:代码父类的引用
a. 静态方法随着类加载而加载(优先于对象),所以静态方法中没有this
b. 通过this()
调用子类的构造函数,super()
调用父类的构造函数 - 字符串对象
- 存储
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()
;String
转StringBuilder
,使用构造方法StringBuilder sb = new StringBuilder(str)
- 集合类,长度可变
- 使用
a. 使用前需要导包
b.ArrayList
E表示泛型(任意引用数据类型,表示你当前集合要存储的引用数据类型)array = new ArrayList (); - 操作
a. 增加:add()
b. 获取 :get(int Index)
c. 删除:remove(Object obj) 返回Boolean
或remove(int Index) 返回obj
d. 修改:set(int Index, Object obj)
返回被修改的内容
e. 长度:size()
- 包
- 用
package
关键字生命,在代码第一行,用来分类管理java文件 - 有多层结构(一个包下有另一个包),不同包下的文件名可以重复
- 相同包下的类可以直接访问;不同包下的类访问时要加上包名,或者使用关键字
import
将类导入
- 内部类:嵌套在其他类内部的类
- 分类:成员内部类(类中),局部内部类(方法中),匿名内部类(实现接口或继承父类并立即创建一个对象,可以用来作为参数传递)
- 内部类在编译时也会有单独的
.class
文件,但是前面会冠以外部类的类名和$符号。内部类是外部类的成员,所以内部类可以访问外部类的成员变量
- 包装类:封装了基本数据类型的类
byte(Byte) short(Short) int(Integer) long(Long) char(Character) float(Float) double(Double) boolean(Boolean)
-
Integer
可以用来转换int
和String
类型的变量
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流
- 概述
- 处理设备之间的数据传输,如在文件中读取数据,或者将数据存储到文件中
- 可以进行文件复制,文件上传,文件下载
- 分类
- 字符输出流
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();
}
}
- 复制文件:也就是先读文件,再写文件
- 字符缓冲流
-
BufferedReader
和BufferedWriter
创建对象时传入一个FileReader
或FileWriter
,其他使用方式同IO流,缓冲流更为高效 - 特殊功能
a.bw.newLine();
写一个换行,会根据系统决定是什么换行符
b.br.readLine();
读一行数据,不包括换行符,返回一个字符串数据
-
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);
}
- 常用集合类:
-
ArrayList
底层结构是数组,查询快,增删慢 -
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);
}
数据结构
- 数组
- 查找方便,直接使用数组索引,但是数组长度和存储值的类型是不可变的,所以增删操作复杂
- 链表:存储当前地址、当前数据和下一个数据的地址
- 查询慢,需要顺着链查找,但是增删快
- 栈和队列
- 栈:先进后出
- 队列:先进先出
异常
- 异常:在代码编译或者运行时出现的错误,异常包括错误的类型、原因和位置
- 体系结构:
Throwable
是所以错误和异常的超类
Throwable(最顶层)
Error(不能处理的严重问题)
Exception(可以处理的问题)
- 异常的处理
- JVM:会把异常输出在控制台上,并终止程序的执行
- 捕获处理
try...catch
try {
// 有可能出错的代码
} catch (Exception e) {
// 代码出错时执行,处理异常
} finally {
// 一定会执行,用于释放资源、处理垃圾的收尾工作
}
- 抛出异常: 当我们无法处理异常时(比如编译时异常),可以抛出异常,谁调用方法谁处理这个抛出的异常,使用关键字
throws
- 异常的分类
- 运行时期异常:
RuntimeException
的子类,在编译时期可以不处理 - 编译时期异常:
Exception
的子类,在编译时期处理
- 自定义异常
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);
}
}
多线程
- 概念
- 进程:一个应用程序在内存中的执行区域
- 线程: 进程中的执行控制单元,一个进程可以由一个线程(单线程:安全性高,效率低),或多个线程(多线程:效率高,存在安全问题)组成
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();
}
}
- 并发问题
a.synchronized
:同步(锁),可以修饰代码块和方法,被修饰的代码块和方法一旦被某个线程访问,则会被锁住,其他线程无法访问
// 效率低
synchronized(锁对象) { // 锁对象需要被所有的线程共享
// 线程代码
}
// 同步方法
public synchronized void method() {
// 锁对象是this,静态方法锁对象是当前类的字节码对象
}
- 线程的生命周期
a. 新建 继承Thread
或实现Runable
接口
b. 就绪:已经具备了执行能力,但是没有执行权利
c. 阻塞、等待(wait() notify()),注意:阻塞状态需要回到就绪状态
d. 运行
e. 死亡
网络编程
Socket
- 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个
Socket
。 - 网络通信其实就是
Socket
之间的通信,数据在两个Socket
间通过IO传输
- 使用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();
}
}
- 使用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();
}
}
扩展
-
Java中的内存分配
- 成员变量和局部变量区别
- 在类中位置不同,成员变量在类中方法外;局部变量在方法中或者方法声明上(形式参数)
- 在内存中位置不同,成员变量在对象中,也就是堆内存中;局部变量在栈内存中
- 生命周期不同,成员变量随着对象存在;局部变量随着方法的调用存在
- 初始化,成员变量无需初始化化,有默认值;局部变量要使用必须初始化