Java基础自查手册
本文最后更新于:2021年6月19日 晚上
一、注释
1. 单行注释
格式: // 注释文字
2. 多行注释
格式: /* 注释文字 */
3. 文档注释
格式: /** 注释文字 */
二、关键字
被Java语言赋予特定含义的单词,组成关键字的字母全部小写。
三、常量
1.概念:
在程序的运行过程中, 其值不会发生改变的量.
2.字面值常量的分类:
- 字符串常量: “abc” 值必须用””括起来
- 字符常量: ‘a’, ‘0’ 值必须用’’括起来
- 整数常量: 1,2,3,4
- 小数常量: 1.2
- 布尔常量: true, false, 值只有两个.
- 空常量: null
3.自定义常量:
采用final关键字修饰的量
四、变量
1.概念
在程序执行的过程中,其值可以在某个范围内发生改变的量。 变量的本质,是内存中的一小块区域
2.定义格式
数据类型 变量名 = 初始化值;
byte b = 10;
int i = 100;
3.数据类型
变量变化的范围就是数据类型。
设计多种数据类型可以帮助我们更充分的利用内存空间,提高内存使用的效率。
注意事项:
- 整形默认是int类型, 定义long类型变量的时候, 后边要加字母L(大小写均可)
- 浮点型默认是double类型, 定义float类型变量的时候, 后边要加字母F(大小写均可)
变量未赋初值, 不能使用;
变量只在它所属的那对大括号内(变量的作用域)有效.
// long类型的数据
long l = 10000000000L;
// float类型的数据
float f = 12.3F;
4.数据类型转换
不同类型的数据由于取值范围不同,存储方式不同,直接进行运算可能会造成数据损失,所以需要将一种类型转换成另外一种类型再进行运算。
public class ConversionDemo1 {
public static void main(String[] args) {
//定义一个int类型的变量 和 一个byte类型的变量
int aa = 2;
byte bb = 3;
System.out.println(aa + bb); //5
//byte cc = aa + bb; //代码报错 涉及到了 自动类型转换.
int dd = aa + bb;
// 目标类型 变量名 = (目标类型)要转换的值;
byte cc = (byte)(aa + bb); //强制类型转换
System.out.println(cc);
System.out.println(dd)
double d = 3.2;
int ee = 2;
double d2 = d + ee;
int i = (int)(d + ee);
System.out.println(d2); //5.2
System.out.println(i); //5 因为丢失了精度.
}
}
五、标志符
1.概念
标识符就是在编程中程序员给类、方法、变量、常量等起的名字
2.组成:
英文大小写字母, 数字, _, $
3.命名规则:
- 类和接口: 所有单词的首字母都大写, 其他小写. HelloWorld, Student
- 方法和变量: 从第二个单词开始, 所有单词的首字母大写, 其他全部小写. getName, studyJava
- 常量: 全部大写, 单词之间用_隔开. MAX_VALUE
- 包名: 全部小写, 多级包之间用.隔开, 一般是公司的域名反写. cn.itcast.demo
- 总结:遵循驼峰命名, 见名知意.
4.注意事项:
不能以数字开头.
不能和Java中的关键字重名.
严格区分大小写.
六、运算符
1.概念
对变量和常量进行运算操作的符号
2.分类
3.注意事项
加号 + 既可以进行数值之间的加法运算(‘a’、‘0’等字符型数据参与运算时,用该字符在计算机中所表示的数值进行运算),也可以用来拼接字符串(int转换为字符串,可采用“” + 1)
++与—算数运算符,其放在变量前,先自增/减,再进行其他操作;其放在变量后,先进行其他操作,再自增/减
+= 可避免进行强制类型转换
short a =10; a += 20; a = (short)(a+20) //直接a = a+20会报错
三元运算符格式为:(关系表达式) ? 表达式1 : 表达式2
// 使用三元运算符计算出三个整数中的最大值 int a = 3; int b = 12; int c = -10; int d = (a>=b)?a:b; int e = (d>=c)?d:c; int f = ( ((a>=b)?a:b) >=c) ? ((a>=b)?a:b):c;
七、选择结构
1.if语句
if (表达式){
// 具体语句
}
if (表达式){
// 具体语句
}else{
// 具体语句
}
if (表达式){
// 具体语句
}else if(表达式){
// 具体语句
} else{
// 具体语句
}
2.switch语句
switch(表达式) {
case 值1:
语句体1;
break;
case 值2:
语句体2;
break;
// ...
default:
语句体n+1;
break;
}
八、循环结构
1.for 循环
for(int i = 0; i<10; i++){ // 初始化语句; 判断条件语句; 控制条件语句
a = i;
}
2.while 循环
/*
初始化语句;
while(判断条件语句) {
循环体语句;
控制条件语句;
}
*/
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
3.do…while 循环
初始化语句;
do {
循环体语句;
控制条件语句;
} while(判断条件语句);
- while小括号后的分号不可省略
- do…while循环的循环体语句至少执行1遍
4.死循环
for(::){}
while(true){}
5.break和continue
- break中断,用于switch语句和循环语句: 在switch语句中,表示结束switch代码块 在循环语句中,表示结束循环
- continue,结束本次循环,继续下次循环
九、方法
1.概念
方法,即函数,是完成特定功能的代码块。可以大大提高代码的复用性。
2.定义格式
修饰符 返回值类型 方法名(参数类型 参数名,参数类型 参数名...){
方法体语句
return 返回值;
}
3.注意事项
- 要定义方法,必须明确返回值类型、方法名、参数类型
4.方法重载
在同一个类中的多个方法,他们的方法名相同,但参数列表不同(参数个数不同、对应位置的参数类型不同)。与返回值、修饰符均无关。
十、数组
1.概念
存储相同类型数据的容器。
数组和集合的区别???
2.定义格式
数据类型 [ ] 数组名 = new 数据类型[长度] //数组长度在定义时指定,不可更改
数据类型 [ ] 数组名 = new 数据类型[]{元素1,元素2,元素3…};
数据类型 [ ] 数组名 = {元素1,元素2,元素3…};
int[] arr = new int[3];
int[] arr = new int[]{1,2,3};
int[] arr = {1,2,3};
// 可通过arr.length获取数组长度
3.访问与赋值
int[] arr = {1,2,3};
arr[2] = 555;
4.注意事项
避免越界、避免空指针;因此,需要提前检测数组长度、检测数组是否非空
十一、面向对象—封装
1.面向对象与面向过程
面向对象是将关注点放在实现某件事的人或某事物身上;面向过程是将注意力放在某件事物的具体实验步骤上面。
比如就洗衣服而言,面向对象可以是 用洗衣机洗衣服、干洗店洗衣服、让妈妈帮忙洗衣服等;面向过程就可以为 打水 -> 放衣服 -> 放洗衣粉 -> 揉搓 -> 晾晒等等。
面向对象思想特征:封装、继承、多态
2.类和对象
类是一个抽象概念,是某些属性和行为的集合;而对象是一种具体存在,是对该类事物的具体体现。
比如手机可以看做一个类,而一加6T就是对象。
3.类的定义与使用
通过类名.brand,类名.model访问成员变量,通过类名.call()访问成员方法
4.成员变量与局部变量
- 类中的变量叫做成员变量(方法外),方法中的变量叫做局部变量;
- 两者的区别:
- 作用定义位置不同(前者在类中、方法外,后者在方法中或者形式参数中),
- 作用范围不同(前者在类中均有效,后者仅在方法中有效),
- 初始化值不同(前者有默认初始化值整形为0,后者无初始化值从而必须先赋值再使用)
- 内存中的位置和生命周期不同(前者存储于堆内存中,随着对象的创建而存在,随着对象的消失而消失;后者存储于栈内存中,随着方法的调用而存在,随着方法的调用完毕而消失)
5.this关键字
当成员变量和局部变量名称一致时,通过 this.名称 来指定成员变量
6.封装概念
将一系列事物共同的行为和属性提取出来,放到一个类或者方法中,隐藏实现细节,仅对外提供公共访问入口。比如setName和getName这种。
- 提高安全性、复用性,将复杂的事情简单化(调用者不知道方法的具体实现;方法可以被重复使用;将繁多的代码以一个方法的方式呈现,仅通过调用方法就可以实现功能,代码维护也变得简单)
- 封装的关键:绝对不能让类中的方法直接访问其它类的数据(属性),程序仅通过对象的方法与对象的数据进行交互
7.private关键字
私有的,用来修饰类的成员,被修饰的内容只能在本类中使用。(一般修饰成员变量)
public,公有的,被修饰的内容可以在任意类中使用;(一般修饰成员方法)
protected
8.构造方法
(1)概述
构造方法的作用为:初始化对象
格式:
修饰符 构造方法名(参数列表) {
//方法体
}
要求:
- 构造方法名必须和类名相同(包括大小写)
构造方法没有返回值(但是里边可以写return)
构造方法没有返回值类型(连void都不能写)
注意:
- 若未提供任何构造方法,系统会给出默认无参构造
- 若已提供任何构造方法,系统不再提供无参构造
- 构造方法可以重载(即支持同时无参构造和有参构造)
(2)分类
空参构造、有参构造
package cn.itcast.HomeWork;
public class Page {
private int page;
public Page() {} // 无参构造
public Page(int page){ // 有参构造,在new对象时需给定初始值
this.page = page;
}
public int getPage(){
return page;
}
public void nextPage(){
this.page = this.page + 1;
}
public void prePage(){
this.page = this.page - 1;
}
}
public class testPage {
public static void main(String[] args) {
Page sc = new Page(1); // 初始化类
int cur_page = sc.getPage();
System.out.println("当前页页码为:"+cur_page);
}
}
十二、面向对象—继承
1.概念
通过扩展一个类来建立另外一个类的过程,叫做继承(inheritance)
所有的类都直接或间接的继承自: java.lang.Object ,被继承的类叫做父类(基类、超类), 继承的类叫做子类(派生类)
2.格式
class 父类 {
// ...
}
class 子类 extends 父类 {
// ...
}
- 子类继承父类后,可拥有父类的非私有方法(成员变量、成员方法)
- 构造方法用于初始化本类对象。
3.使用场景
(1)向上抽取
多个类中存在相同的属性和行为时,可以将这些内容提取出来放到一个新类中,让这些类和新类产生父子关系,实现 代码复用。
(2)向下拓展
当需要扩展已有的类的功能时,可以通过继承已有的类,在子类中添加新功能或重新实现已有功能,对父类(已有的类)没有影响。
4.优缺点
程序设计应该努力做到:低耦合、高内聚;耦合是指两个或者多个程序(或者模块)互相依赖
5.继承关系中类成员
(1)同名的成员变量
查找变量的原则: 就近原则
- 查找变量的顺序: 局部变量 → 成员变量 → 父类 → 更高的父类…Object
访问父类变量的方式: super.父类变量名;
- super: 当前对象父类的引用(父类内存空间的标识)
对象初始化顺序: 先初始化父类内容,再初始化子类内容
(2)同名的成员方法
查找方法的原则: 就近原则
查找方法的顺序: 本类 → 父类 → 更高的父类…Object
访问父类方法的方式: super.父类方法名();
定义重名方法的前提: 父类功不能完全满足现实需求,扩展父类功能;父类功能已过时,重新实现父类功能
6.构造
子类创建对象时,必须先初始化该对象的父类内容,若父类中不存在默认无参构造,须手动调用父类其它构造。
7.Java中继承的特点
- Java仅支持类的单继承,不支持多继承。但支持多重(层)继承
- Java支持接口的多继承
- 构造方法用于初始化本类对象。因此无法继承构造方法
- Java中子类仅可继承父类的非私有方法(成员变量、成员方法)。
- 继承会体现一种“is a”的关系,如红富士是一种苹果,苹果是一种水果。
// 类的单继承
public class Fruit{
}
public class Apple extends Fruit{ // 类的单继承
}
public class Fuji extends Apple{ // 多重(层)继承
}
// 接口的多继承
// 接口A extends 接口B,接口C,接口D
8.方法重写(override)
常用于拓展父类功能,或者父类功能过时时,重写父类功能。但父类私有方法不能重写。
十三、面向对象—多态
1.概述
多种状态,即同一对象在不同情况下,有不同的状态
2.实现步骤
- 要有继承或者实现关系,
子类重写父类方法
父类引用 指向 子类对象
public class Test{
public static void main(String args[]){
// 父类引用Animal 指向 子类对象Dog
Animal a = new Dog();
}
}
- 父类型变量作为参数时,可以接受任意子类型象
public static void main(String[] args){
Dog dog = new Dog();
dog.setName("布鲁斯");
showAnimal(dog); // 输出: 布鲁斯吃骨头
}
public static void showAnimal(Animal animal){ // 父类型变量作为参数
animal.eat();
}
多态关系中,成员变量无法重写,调用成员变量, 遵循“编译看左,运行看左”。
- 编译看左: 意思是在编译期间会看左边的类型有没有这个成员, 没有就报错, 有就不报错.
- 运行看左: 意思是在运行期间使用的是 左边的类型中的这个成员.
public static void main(String[] args){
Animal a = new Dog(); // 多态
Dog d = new Dog();
System.out.println(a.name); // 输出: Animal,编译看左,运行也看左
System.out.println(d.name); // 输出: Dog
}
public class Animal{ // 父类型
String name = "Animal";
}
public class Dog extends Animal{ // 子类型
String name = "Dog";
}
- 多态关系中,必然有成员方法是重写的。在调用成员方法时,遵循“编译看左,运行看右”。
/*
动物类案例:
已知父类Animal, 成员变量为: 姓名, 成员方法为: eat()方法,一子类Dog类。
*/
public class Test {
public static void main(String[] args) {
//多态
Animal an = new Dog();
//测试成员方法的调用
// 多态中调用成员方法:编译看左(左边的类型有没有这个成员)
an.setName("哈士奇"); // 运行看右(运行时具体用的是右边类中的该成员).
an.eat(); // 此处调用Dog类的eat方法
}
}
3.优缺点
(1)增强可维护性。
基于继承关系,只需要维护父类代码,提高了代码的复用性,大大降低了维护程序的工作量。
封装:隐藏数据的实现细节,让数据的操作模块化,提高代码复用性
继承:复用方法,从对象的行为这个层面,提高代码的复用性
多态:复用对象,程序运行时同一个对象表现出不同的行为不是很懂…
(2)增强可拓展性。
把不同的子类对象都当作父类看待,屏蔽了不同子类对象间的差异,做出通用的代码,以适应不同的需求,实现了向后兼容。
(3)多态无法使用子类特有成员,要使用需向下转型。
- 只能在继承层次内进行转换, 否则会报ClassCastException异常
- 将父类对象转换成子类之前,使用instanceof进行检查:
对象名 instanceof 数据类型
public class Test {
public static void main(String[] args) {
//需求: 通过多态创建对象, 调用子类中的成员.
Animal an = new Dog();
//调用eat()方法
an.eat();
//调用watch()方法, 属于子类独有的方法.
//an.watch();
//正确的写法
/*Dog dog = (Dog)an;
dog.watch();*/
//不正常的转换.
//Cat c = (Cat)an;
//优化后的方案: 判断当前对象是否是Dog类的对象, 如果是, 再调用watch()方法.
if(an instanceof Dog) { //判断an是否是Dog类的对象
//能走到这里, 说明条件满足
Dog dog = (Dog)an;
dog.watch();
}
}
}
向上转型(自动):子类型转换为父类型,如Animal animal = new Dog();
向下转型(强制):父类型转换为子类型,如Dog dog = (Dog)animal;
十四、抽象类 abstract
1.概念
- 只有方法声明,没有具体实现的方法称为抽象方法,用abstract修饰
- 用abstract修饰,无法实例化的类称为抽象类。
修饰符 abstract class 类名 {}
修饰符 abstract 返回类型 方法名 {}
2.抽象类特点:
(1)抽象类与抽象方法辨析
- 抽象类可以没有抽象方法,但是如果一个类被定义为抽象lei,即使其没有抽象方法,也不能被实例化,即无法直接构建一个该类的对象;
- 如果一个类中包含抽象方法,则该类必须被定义为抽象类,用abstract修饰。
(2)抽象类无法直接被实例化
- 抽象类是不具体的,没有方法体,即提供的成员无法生成一个具体对象。但一个不具体的对象是无法生成的。比如,我们可以实例化一个苹果(具体存在的),但无法实例化一个水果(一个抽象概念)
- 从内存方面考虑的话,对象实例化时,关键词new会向JVM申请内存,这个类的成员(成员变量、成员方法)会被保存到内存中。而抽象类,没有具体的成员,因此无法准确分配内存。
- 可以通过创建抽象类的非抽象子类来实例化对象。(子类重写了父类抽象类的所有方法)
(3)抽象类的子类
- 如果是普通类,则必须重写父类所有抽象方法。
- 也 定义成抽象类,则不必全部重写。
4.抽象类成员的特点
- 成员变量:可以有普通的成员变量,也可以有成员常量(final)
- 成员方法:可以有普通方法,也可以有抽象方法。
- 构造方法:像普通类一样有构造方法,且可以重载
5.final 关键字
意思为最终的,即被修饰的元素无法被修改
- 修饰类时,该类无法被继承
修饰方法时,该方法不能被重写,无法与abstract共存(抽象方法必然被重写)
修饰变量时,变为常量,只能赋值一次。
6.static 关键字
静态的,用来修饰类、成员方法、成员变量
- 被static修饰的成员变量,称为静态变量(类变量),被本类所有对象所共享。可以通过
类名.成员变量名
的形式直接访问 - 成员变量包括实例变量、类变量(静态变量)
被static修饰的成员方法,称为静态方法
- 没有对象this
- 无法访问非静态成员;但非静态方法中,是可以访问静态成员方法/变量的。
- 无法被声明为abstract。
- 通过
类名.成员方法名(参数)
调用。 - 静态方法的运行优先级高于main,也高于构造方法。
执行顺序:
父类静态代码块 ->子类静态代码块 ->父类非静态代码块 -> 父类构造函数 -> 子类非静态代码块 -> 子类构造函数。
十五、接口
1.接口定义
接口用来提供统一的规则、规范
interface 接口名 {
}
// 接口和类是实现关系,用implement表示
class 类名 implements 接口名{
}
2.接口的特点
(1)接口没有成员变量,默认有public static final
修饰,即只有成员常量。
(2)接口中的成员方法默认都有public abstract
修饰,因此接口是抽象的,无法实例化,但可以通过多态的方式实例化子类对象。
public abstract 返回值类型 方法名(){}
// JDK8之后,可以有默认方法和静态方法
public default 返回值类型 方法名(){}
static 返回值类型 方法名() {}
// JDK9之后,可以有私有方法
private 返回值类型 方法名(){}
(3)接口没有构造方法(接口无成员变量–都是常量,无需要初始化的具体方法–抽象的,因此无需要初始化的成员)
(4)接口的实现类 要重写接口的所有抽象方法,或者 声明为抽象类
(5)接口直接可以多继承。(类只能单继承,但可以多重/层继承)
interface 接口1 extends 接口2,接口3,接口4...{ }
十六、常用API
1.Object类
// 类层次结构中最顶层的类,所有类都直接或者间接的继承自Object类
// hashCode() 返回该对象的哈希码值(通过对象地址值计算得到)
// toString() 返回该对象的字符串表示
// equals() 返回其他某个对象是否与此对象相等(比较地址值),必须重写
2.Scanner类
import java.util.Scanner;
// 创建对象
Scanner sc = new Scanner(System.in);
// 接受字符串类型的数据(以 换行符 作为分隔)
String s1 = sc.nextLine();
// 接受字符串类型的数据(以 空白字符 作为分隔,如空格,tab,回车等)
String s2 = sc.next();
// 判断是否有下一个输入项
if (sc.hasNextInt()){
int num = sc.nextInt();
}
3.Random类
import java.util.Random;
Random r = new Random();
int num = r.nextInt(10); // 获取0~9范围内的随机数
int num2 = r.nextInt(100) + 1; //获取1~100范围内的随机数
4.String类
/**
定义字符串
*/
// 将 指定的字节数组 转换为 字符串
byte[] bys = {1,2,3};
String s1 = new String(bys);
// 将 制定的字符数组 转换为 字符串
String[] chs = {'a','b','c'};
String s2 = new String(chs);
String s3 = "abc";
String s4 = "" + 0;
// 判断两个字符串是否相同,区分大小写
boolean b1 = str1.equals(str2);
// 不区分大小写
boolean b2 = str1.equalsIgnoreCase(str2);
// 判断是否以给定字符串开头
boolean b3 = str1.startwith("abc");
// 判断字符串是否为空
boolean b4 = str1.isEmpty();
// 获取当前字符串的长度
int len = str1.length();
// 获取指定字符(串)第一次出现的索引
int index1 = str1.indexOf('a');
// 获取指定字符(串)最后一次次出现的索引
int index2 = str1.lastIndexOf('a');
// 获取指定索引位置处的字符
char ch = str1.charAt(1);
// 获取指定索引位置(含)之后的字符串
String s5 = str.substring(5);
// 获取从索引start位置(含)起至索引end位置(不含)的字符串
String s6 = str.substring(5,10);
/*
String类 的转换功能
*/
String s1 = "abc";
// 1.将 字符串 转换成 字节数组
byte[] bys = s1.getBytes(); //97, 98, 99。直接打印数组的话,显示的是地址值
// 2.将 字符串 转换成 字符数组
char[] chs = s1.toCharArray(); //'a','b','c'
// 3.将指定类型数据转换成字符串
String s2 = String.valueOf(123); // "123"
String s3 = "" + 123; // 更常用一些
// 4.将指定字符(串)替换成新的字符(串)
String s4 = "abc abc abc";
String s5 = s4.replace('b','d'); //'d' 替换 'b'
// 5.切割字符串,返回切割后的字符串数据,原字符串不变
String[] arr = s4.split(" ");
// 6.去掉字符串两端的空白字符
String s6 = " a b c ";
String s7 = s6.trim();
5.StringBuilder类
/*
StringBuilder:
简介
可变字符序列,用于构造字符串对象。内部使用『自动扩容的数组』操作字符串数据。
传统的String无法多次修改。
构造方法
StringBuilder(): 构造一个空的StringBuilder容器
StringBuilder(String): 构造一个StringBuilder容器,并添加指定字符串
成员方法
StringBuilder append(…): 将任意数据添加到StringBuilder容器中, 返回自身
String toString(): 将 当前StringBuilder容器 转成 字符串
*/
public class Test {
public static void main(String[] args) {
// 1.空参构造
StringBuilder sb = new StringBuilder();
StringBuilder sb2 = sb.append("abc");
// 2.带参构造
StringBuilder sb3 = new StringBuilder("abc");
//需求: 将三个字符串拼接成一个新的字符串: 学Java, 到传智播客 找小黑!
StringBuilder sb4 = new StringBuilder();
sb4.append("学Java,");
sb4.append("到传智播客");
sb4.append("找小黑!");
System.out.println("sb4: " + sb4);
System.out.println("----------------");
String s = sb4.toString(); // 转换为字符串
System.out.println("字符串s: " + s);
}
}
6.Date类
/*
简介:
日期类,用于操作时间相关信息。
构造方法:
Date(): 构造一个日期对象,当前系统时间,精确到毫秒
Date(long): 构造一个日期对象,时间为自“1970年1月1日00:00:00 GMT”起,至指定参数的毫秒数
成员方法:
long getTime(): 将日期对象转换成对应时间的毫秒值
*/
import java.util.Date;
public class Test {
public static void main(String[] args) {
//测试空参构造, 采用当前操作系统的默认时间
Date date1 = new Date();
System.out.println("date1:" + date1);
//获取当前操作系统时间的毫秒值
long time = date1.getTime();
System.out.println("time:" + time);
//Sun Jun 06 17:04:39 CST 2066 --> 3043040679456
//创建一个指定的时间
Date date2 = new Date(3043040679456L);
System.out.println("date2:" + date2);
}
}
7.Calender类
/*
Calendar类:
简介:
日历类. 用于操作日期相关信息。
成员方法
static Calendar getInstance(): 根据当前系统时区和语言环境获取日历对象
int get(int field): 返回给定日历字段的值
void set(int field, int value):将给定的日历字段设置为指定的值
*/
import java.util.Calendar;
public class Test2 {
public static void main(String[] args) {
// 创建Calendar类型的对象.
Calendar c = Calendar.getInstance();
// 获取年月日的信息
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH); //Java中使用0-11的数字表示月份的, 对应1-12月
int day = c.get(Calendar.DATE);
// 设置指定时间为: 2022年2月2日
c.set(2022, 1, 2);
}
}
8.SimpleDateFormat类
/*
SimpleDateFormat类
构造:
*/
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo2 {
public static void main(String[] args) throws ParseException {
Date date = new Date();
System.out.println("初始日期格式为:" + date);
SimpleDateFormat dtf = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("第一种时间格式为:" + dtf.format(date));
SimpleDateFormat dtf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("第二种时间格式为:" + dtf2.format(date));
SimpleDateFormat dtf3 = new SimpleDateFormat("yyyy年MM月dd日");
System.out.println("第三种时间格式为:" + dtf3.format(date));
System.out.println();
String str = "2020-03-18 12:00";
SimpleDateFormat dtf4 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date newDate = dtf4.parse(str); // 将字符串解析为时间格式
System.out.println(str + " -----> " + dtf3.format(newDate));
DateUtil("12",date);
}
public static void DateUtil(String args, Date date){
SimpleDateFormat dtf1 = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat dtf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat dtf3 = new SimpleDateFormat("yyyy年MM月dd日");
if(args == "12"){
System.out.println(date + " -----> " + dtf3.format(date));
}
}
}
十七、集合
1.概念
集合是指用来存储多个元素的容器。相比于固定长度的int[]
这种类型的数组,其可以任意扩容
(1)单列集合Collection
有List
和Set
两个常用接口,其中List
的常用实现类为ArrayList
,元素有序且可重复;Set的常用实现类为HashSet
,元素无序且不可重复;
(2)双列集合Map
的实现类为HashMap
,其元素由键值对 key--value
构成,其中key不可重复,value可重复。
2.单列集合之List集合
- 元素有序(元素的存取顺序一致),可重复。
- 通过创建其子类
ArrayList
对象来完成List接口的实例化。List<> list = new ArrayList<>();
- List接口中的成员方法:
- public boolean
add
(E e); // 将数据添加到集合的末尾, 这里的E是泛型的意思, 目前可以先理解为Object类型. - public E
get
(int index); // 根据索引, 索取其对应的元素. - public int
size
(); // 获取集合的长度.
- public boolean
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
//1. 创建集合对象.
List list = new ArrayList();
//2. 创建元素对象.
Student s1 = new Student("乔峰",41);
Student s2 = new Student("乔峰",41);
Student s3 = new Student("虚竹",38);
Student s4 = new Student("段誉",26);
//3. 将元素对象添加到集合对象中.
/*boolean b1 = list.add(s1);
System.out.println(b1);
*/
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
//直接打印集合
System.out.println(list);
//4. 遍历集合. 通过size()获取集合长度
for (int i = 0; i < list.size(); i++) {
Object obj2 = list.get(i);
System.out.println("索引为 " + i + "的元素是: " + obj2 );
}
}
}
3.增强for循环
/*
增强for格式:
for(元素的数据类型 变量名 : 要遍历的数组或者集合对象) {
//循环体, 变量也就是元素
}
快捷方式:
iter --> 回车
注意:
增强for的底层依赖的是迭代器(Iterator).
大白话解释: 增强for就是迭代器的简写形式.
*/
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
//1. 创建集合对象.
List list = new ArrayList();
//2. 将元素对象添加到集合对象中.
list.add(10);
list.add(10); // ArrayList中元素可重复
list.add(30);
list.add(20);
list.add(40);
//3. 遍历集合.
for(Object obj : list) {
// obj是集合中的元素, 其本身应该是Integer类型的数据.
Integer ii = (Integer)obj;
System.out.println(ii);
}
}
}
4.迭代器
迭代器是遍历collection集合的通用形式。
(1)常用方法
next()
:返回迭代的下一个元素对象hasNext()
:如果仍有元素可以迭代,则返回true
(2)使用步骤
根据集合对象获取其迭代器对象
判断迭代器是否仍有元素
- 如果有就获取元素
Iterator it = list.iterator();
while(it.hasNext()) {
String s = (String)it.next();
System.out.println(s);
}
(3)分类
- 普通的迭代器
iterator
在遍历集合的同时不能添加或者删除元素, 否则会报: 并发修改异常. - 列表迭代器
listIterator
(List体系独有)在遍历集合的同时,还可以修改集合中的元素(添加, 删除等),,但必须使用列表迭代器中的方法.
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Test1 {
public static void main(String[] args) {
//需求: 测试列表迭代器
// 1. 创建集合对象.
List list = new ArrayList();
// 2. 将元素对象添加到集合对象中.
list.add("a");
list.add("b");
list.add("c");
//需求: 判断集合中如果有字符串"b", 就在其后边添加一个新的字符串: java
//1. 根据集合对象获取列表迭代器对象.
ListIterator lit = list.listIterator(); // 列表迭代器
//2. 判断迭代器中是否有元素.
while(lit.hasNext()) {
//3. 有就获取元素即可.
String s = (String)lit.next();
if ("b".equals(s)) {
//list.add("java"); //这样写不行, 必须调用列表迭代器的方法来实现.
lit.add("java"); // 重点!!
}
}
//打印新的集合中的值
System.out.println(list);
}
}
5.泛型
泛型,即泛指任意类型,又叫参数化类型(Parameterized Type),对具体类型的使用起到辅助作用,类似于方法的参数。
比如:
List<String> list2 = new ArrayList<>();
集合类泛型,表示该集合中存放指定类型的元素
- 优点:保证类型安全,也避免了类型的转换
public class Test {
public static void main(String[] args) {
//不使用泛型的集合
List list1 = new ArrayList();
list1.add("a");
list1.add("b");
list1.add("c");
System.out.println(list1);
//list1.add(10); 会报类型转换异常(ClassCastException)
for (Object obj : list1) { // 增强for里面为Object类型数据
String s = (String)obj; // 需进行类型转换
System.out.println(s);
}
System.out.println("----------------");
//需求: 演示泛型
//1. 创建集合对象.
List<String> list2 = new ArrayList<>();
list2.add("abc");
list2.add("bcd");
list2.add("cde");
for (String s : list2) { // 增强for里面直接为String型数据了
System.out.println(s); // 无需进行类型转换
}
}
}
6.Collections工具类
Collections.max(集合名)
:返回该集合的最大元素Collections.sort(集合名)
:对集合进行排序(默认升序)Collections.reverse(集合名)
:反正集合元素,搭配sort可实现降序排列Collections.shuffle(集合名)
:随机打乱集合元素
7.单列集合之Set集合
Set集合中元素不可重复、无序
创建集合对象:
Set<T> set = new HashSet<>();
添加元素:
set.add("1");
遍历集合:
// 迭代器 Iterator<Student> it = set.iterator(); while(it.hasNext()){ Student s = it.next(); System.out.println(s); } // 增强for for(Student student: set){ System.out.println(student); }
Set集合保证元素的唯一性依赖:
equals()
、hashCode()
两个方法。默认调用的是Object类中的equals方法(比较地址值是否相同),因此需要重写equals和hashCode方法!!
8.双列集合之Map集合
双列集合,元素由键值对(Entry)构成:
key
—value
- key不可以重复,但value可以重复。
创建对象:
Map<T1, T2> map = new HashMap<>();
T1和T2分别表示key和value的数据类型
添加元素:
map.put(key,value)
;- 元素第一次添加,会返回null;重复添加时,新值覆盖旧值,并返回旧值
根据key获取其对应的value:
map.get(key)
;遍历集合:
// 增强for Set<Interger> keys = map.keySet(); // 获取所有的键 for(Interger key: keys){ Student val = map.get(key); // 根据key获取其对应的value System.out.println(val); } // 迭代器 Set<Interger> keys = map.keySet(); // 获取所有的键 Iterator it = keys.iterator(); while(it.hasNext()){ Integer key = it.next(); // 获取迭代器的数据 Student val = map.get(key); // 根据key获取其对应的value System.out.println(val); }
十八、异常
1.概念
异常,即非正常的情况,也就是程序出现的错误
2.分类
顶层父类: Throwable
- 异常(Exception),如NullPointerException
- 错误(Error),如StackOverFlowError
3.处理方式
捕获异常,自己处理
try..catch..finally
- finally代码块可以省略,但不能和catch部分同时省略;
- finally代码之前若有return语句,
先执行return语句
,再执行finally代码块
,最后返回return的结果 - 方法重写时,子类方法不能抛出比父类方法更大的日常。
try { // 尝试执行的代码 } catch(Exception e) { // 出现可能的异常之后的处理代码 } finally { // 一定会执行的代码,如关闭资源 } // 多个异常分别处理 try { // 尝试执行的代码 } catch(异常A e) { // 出现可能的异常之后的处理代码 } catch(异常B e) { // 出现可能的异常之后的处理代码 } finally { // 一定会执行的代码,如关闭资源 }
抛出, 交给调用者处理.
throws
public void 方法名() throws Exceptoin { } // 调用该方法的调用者必须处理这个异常,try..catch..捕获,或者 继续抛出日常
十九、IO流
1.概念
输入、输出流,可用于在本地磁盘、网络上读写数据
2.分类
(1)字符流:按字符(a~z, 0~9)读写数据
(2)字节流:按字节(0,1)读写数据
3.File类
(1)概念
一个File对象代表磁盘上的某个文件或者文件夹
(2)构造方法
// 1.根据给定的字符串路径创建其对应File对象:File(String pathname)
File file1 = new File("D:/abc/1.txt");
// 2.根据给定的字符串形式的父目录和子文件(夹)名创建File对象:File(String parent, String child)
File file2 = new File("D:/abc/", "1.txt");
// 3.根据给定的父目录对象和子文件(夹)名创建File对象:File(File parent, String child)
File file3 = new File("D:/abc/");
File file4 = new File(file3, "1.txt");
(3)成员方法
- 创建功能:
createNewFile()
:创建文件mkdir()
和mkdirs()
:创建单级、多级目录
- 判断功能:
isDirectory()
:判断File对象是否为目录isFile()
:判断File对象是否为文件exists()
:判断File对象是否存在
- 获取路径、名称等:
getAbsolutePath()
:获取绝对路径- 从本地磁盘开始的路径,如
C:\Users\itcast\Desktop
- 从本地磁盘开始的路径,如
getPath()
:获取文件的相对路径- Java项目中,相对路径从项目名开始
getName()
:获取文件名list()
:获取指定目录下所有文件(夹)名称数组listFiles()
:获取指定目录下所有文件(夹)File数组
4.字符流读写文件
以字符为单位来操作数据。如纯文本文件等
Reader: 字符输入流的顶层抽象类.
FileReader: 普通的字符输入流.
BufferedReader: 高效的字符输入流(也叫: 字符缓冲输入流)Writer: 字符输出流的顶层抽象类.
FileWriter: 普通的字符输出流.
BufferedWriter: 高效的字符输出流(也叫: 字符缓冲输出流)
(1)读
步骤:创建文件对象,读入数据,关闭资源
按单个字符读取:
read()
- 返回读到的字符
按字符数组读取:
read(char[] chs)
- 返回读取到的有效字符数,读不到返回-1.
// 创建普通字符输入流
Reader reader = new FileReader("lib/1.txt");
int ch; // 接收读到的单字符
while((ch = reader.read()) != -1){ // 三步:读取字符,赋值,判断
System.out.println(ch);
}
reader.close(); // 释放资源.
Reader reader = new FileReader("lib/2.txt");
char[] chs = new char[3];
int len;
while((len = reader.read(chs)) != -1) { // 三步:读取字符存到chs中,返回值赋给len,比较
String s = new String(chs,0,len); // 0: 表示起始索引;len: 表示要操作的字符的个数.
System.out.println(s);
}
reader.close();
(2)写
步骤:创建文件对象、写入数据、关闭资源
- 一次写一个字符:
write(char)
- 一次写一个指定的字符数组:
write(char[] chs, int index, int len)
- 一次写一个字符串
Writer w = new FileWrite("lib/1.txt"); // 创建对象
w.write('好');
w.write("你好!");
char[] chs = {'你','好','!'};
w.write(chs,0,2); // 索引从0开始,后面两个字符
w.close(); // 关闭资源
(3)拷贝文件
步骤:创建字符流读、写文件对象,读取数据,写入数据,关闭资源
// 一个字符一个字符的来
FileReader fr = new FileReader("lib/1.txt");
FileWriter fw = new FileWriter("lib/3.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
int len;
while((len = fr.read()) != -1) {
fw.write(len);
}
fr.close();
fw.close();
// 一组一组的来
FileReader fr = new FileReader("lib/1.txt");
FileWriter fw = new FileWriter("lib/3.txt"); //细节: 如果目的地文件不存在, 程序会自动创建
char[] chs = new char[1024];
int len;
while((len = fr.read(chs)) != -1) {
fw.write(chs,0,len);
}
fr.close();
fw.close();
- 字符缓冲流拷贝文件
// 创建字符缓冲对象,并关联响应文件
BufferedReader br = new BufferedReader(new FileReader("lib/1.txt")); // 输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("lib/2.txt")); // 输出流
// 一次读取一个字符,读不到返回-1
int len;
while((len = br.read()) != -1){
bw.write();
}
br.close(); // 关闭的是br和bw
bw.close();
// 创建字符缓冲对象,并关联响应文件
BufferedReader br = new BufferedReader(new FileReader("lib/1.txt")); // 输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("lib/2.txt")); // 输出流
// readLine()一次读取一行数据,返回读到的内容,读不到时返回null
String str;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine(); // !!要换行,在linux中相当于\n,windows中相当于\r\n,mac中为\r
}
br.close(); // 关闭的是br和bw
bw.close();
5.字节流读写文件
以字节为单位来操作数据。如音视频文件等
InputStream: 字节输入流的顶层抽象类.
FileInputStream: 普通的字节输入流.
BufferedInputStream: 高效的字节输入流(也叫: 字节缓冲输入流)OutputStream: 字节输出流的顶层抽象类.
FileOutputStream: 普通的字节输出流.
BufferedOutputStream: 高效的字节输出流(也叫: 字节缓冲输出流).
和字符流差不多,比着来就好
// 创建 普通字节流读文件对象、普通字节流写文件对象:
InputStream is = new FileInputStream("Desktop.jpg");
OutputStream os = new FileOutputStream("D:\\博学谷桌面.jpg");
// 创建 缓冲字节流读文件对象、缓冲字节流写文件对象:
BufferedInputStream bi = new BufferedInputStream(new FileInputStream("Desktop.jpg"));
BufferedOutputStream bi = new BufferedOutputStream(new FileOutputStream("D:\\博学谷桌面.jpg"));
// 定义字节数组,每次读取2048个字节
byte[] b = new byte[1024];
int len;
while((len = is.read(b)) != -1){
os.write(b, 0, len);
}
is.close();
os.close();
二十、反射
二十一、多线程
(1)定义
进程是指正在运行的程序,而线程是指进程中的执行路径
- 单线程:一个进程只有一条执行路径。比如记事本
- 多线程:一个进程有多条执行路径。比如扫雷
(2)实现方式
- 继承 Threads类
- 定义一个类MyThread 继承Thread类
在 MyThread类中重写run()方法
- run方法用来封装被线程执行的代码。
- start()方法:启动线程,然后由JVM调用此线程的run()方法
- 创建 MyThread类的对象
- 启动线程
实现Runnable接口
- 定义一个类 MyRunnable实现 Runnable接口
- 在 MyRunnable类中重写run()方法(无法直接使用getName方法)
- 创建 Myrunnable类的对象
- 创建 Thread类的对象,把 Myrunnable对象作为构造方法的参数
- 启动线程
相比继承 Thread类,实现 Runnable接口的好处
- 避兔了Java单继承的局限性
- 适合多个相同程序的代码去处理同个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
(3)设置和获取线程名称
通过
setName
设置线程名称,通过getName
获取线程名称通过带参构造设置线程名称,但需要在父类中重新定义带参构造
也可通过
cuurentthread
方法,先得到线程对象再获取当前线程名称
(4)线程调度
目前主要有两种线程调度模型:
- 分时调度模型:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择个,优先级高的线程 获取的CPU时间片相对多一些
Java采用的是,抢占式调度模型。线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多或者多次运行的时候才能看到你想要的效果
通过
setPriority(int newPriority)
设置线程优先级(默认为5,最小值为1,最大10)通过
getPriority()
获取线程优先级
(5)线程控制
join:等待这个线程死亡
sleep:使当前线程暂停n毫秒
setDaemon:将此线程标记为守护线程()
(6)线程的生命周期
(7)示例:卖票
需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计个程序模拟该电影院卖票
思路:
(8)synchronized解决数据安全问题
- 同步代码块
同步方法时,直接将synchronized关键字
加到方法名上即可。
- 同步方法的锁对象是
this
,但静态方法是木得this的所以要通过类名.class
的方法进行上锁。 - 修饰符 (static) synchronized 返回值类型 方法名(方法参数) { }
线程安全的类有:
StringBuffer
:线程安全、可变的字符序列。从版本JDK5开始,被StringBuilder
替代。通常应该使用 StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步。Vector
:从Java2平台v1.2开始,该类改进了List接口,使其成为 Java Collections Framework的成员。与新的集合实现不同, Vector被同步。如果不需要线程安全的实现,建议使用 Arraylist代替 VectorHashtable
: 该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值。从Java2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为 Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。如果不需要线程安全的实现,建议使用 Hashmap代替 Hashtable
多线程环境下,为了满足安全性需求,采用StringBuffer替代StringBuilder,但线程安全的vector和hashtable并不常用,常用的是Collections里的synchronizedList、synchronizedMap
(9)Lock锁
(10)生产者/消费者模式
二十二、网络编程
(1)基础概念
三要素:IP地址、端口、协议:
(2)IP地址
- IPV4:32位(bit)地址,4个字节,二进制,如
1000000101010000000000101000010
,长表示为十进制形式,即192.168.1.66 - IPV6:128位地址,每16位一组,组成8组16进制数。
InetAddress类,表示Internet协议(IP)地址
getByName(String host)
:确定主机名称的IP地址getHostName()
:获取此IP地址的主机名getHostAddress()
:获取文本显示中的IP地址字符串
(3)端口
- 端口:设备上应用程序的唯一标识
- 端口号:用两个字节表示的整数,它的取值范围是
0~65535
。其中,0~1023
之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败、
(4)协议
- UDP协议:用户数据报协议,面向无连接,无法保证数据的完整性;但资源消耗少,通信效率高
- TCP协议:传输控制协议(Transmission Control Protocol),面向连接,可靠无差错,三次握手。
- 第1次握手,客户端向服务器端发出连接请求,等待服务器确认
- 第2次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
- 第3次握手,客户端再次向服务器端发送确认信息,确认连接
🚣♀️ UDP发送数据
- 创建发送端的Socket对象:
DatagramSocket ds = new DatagramSocket();
- 创建数据,并把数据打包:
byte[] bys = "Hello,UDP".getBytes();
InetAddress address = InetAddress.getByName("192.168.1.66");
DatagramPacket dp = new DatagramPacket(bys, bys.length, address, 10086);
- 调用DatagramSocket对象的方法发送数据:
ds.send(dp);
- 关闭客户端:
ds.close();
🚣♀️ UDP接收数据
- 创建接收端的 Socket对象:
DatagramSocket ds = new DatagramSocket(10086); // 构建数据报套接字,并将其绑定到本地主机上的特定端口上
- 创建一个数据包,用于接收数据:
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
- 调用 Datagramsocket对象的方法接收数据:
ds.receive(dp);
- 解析数据包,并把数据在控制台显示:
byte[] datas = dp.getData();
String dataString = new String(datas, 0, dp.getLength());
System.out.println(dataString);
- 关闭接收端:
ds.close();
🚣♀️UDP练习
// SendDemo.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendDemo {
public static void main(String[] args) throws IOException {
// 创建发送端的DataSocket对象
DatagramSocket ds = new DatagramSocket();
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;
while ( (line= br.readLine())!=null ) {
if("886".equals(line)){
break;
}
// 创建数据,并打包(地址为本机ip地址,注意修改)
byte[] bys = line.getBytes();
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("172.18.154.202"), 12345);
ds.send(dp);
}
// 关闭客户端
ds.close();
}
}
// ReceiveDemo.java,支持开启多个发送界面,该接收端均能收到
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(12345);
while(true){
// 创建数据包,用于接收数据
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
// 调用DatagramSocket对象的方法接收数据包
ds.receive(dp);
// 解析数据包,并显示
System.out.println("数据是:" + new String(dp.getData(), 0, dp.getLength()));
}
}
}
📟 TCP发送数据
- 创建客户端的Socket对象:
Socket s = new Socket(“172.18.154.202”, 10000);
- 获取输出流,写数据:
OutputStream os = s.getOutputStream(); os.write("hello, TCP".getBytes());
- 关闭对象:
s.close();
📟 TCP接收数据
- 创建服务器端的 Socket对象( ServerSocket):
ServerSocket ss = new ServerSocket(10000);
- 监听要连接的套接字,返回一个Socket对象:
Socket s = ss.accept();
- 获取输入流,读数据,并把数据显示在控制台:
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
String data = new String(bys, 0, is.read(bys));
- 释放资源:
s.closer(); ss.close();
客户端,先写数据,后读数据;服务器端,先读数据,后写数据。
二十三、lambda表达式
(1)组成三要素
形式参数、箭头、代码块
(2)使用前提
- 有一个接口,且接口中有且只有一个抽象方法(接口中的方法默认有public abstract)
(3)练习
(4)省略模式
- 参数类型可以省略,但是有多个参数时,要一省全省。
- 如果参数有且仅有一个,小括号也可以省略。
- 如果代码块中的语句只有一条,可以省略大括号和分号。(如果有return,return也要省略掉)
(5)注意事项
- 使用lambda必须有接口,且接口中有且只有一个抽象方法
- 必须有上下文环境,才能推导出lambda对应的接口
(6)和匿名内部类的区别
- 所需类型不同:
- 匿名内部类:可以是接口、抽象类、具体类
- Lambda表达式:只能是接口
使用限制不同:
- 如果接口中有且只有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类;
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式。
实现原理不同:
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,无单独字节码文件生成,对应的字节码会在运行时动态生成。
二十四、Stream流
其他
1. Java中有没有真正意义的引用传递?
- 值传递( pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数
- 引用传递( pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数
2.访问权限修饰符
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!