文章目录
前言
本文分享了一些Java语法的基础, 有详有略, 供大家查阅
标识符
- 字母,下划线_, 美元符号$开头
- 字母,下划线,美元符号任意组合
- 长度不限,大小写敏感
- 不可以是关键字
标识符规范
- 表示类名:每个单词首字母大写
- 表示方法和变量:驼峰规则
- java不采用ASCII字符集,而是Unicode编码
数据类型
变量
类型 | 声明位置 | 从属于 | 生命周期(作用域) |
---|---|---|---|
局部变量 | 方法或语句块内部 | 方法/语句块 | 从声明处开始,到方法或语句块结束 |
成员变量(实例变量) | 类内部,方法外部 | 对象 | 对象创建,成员变量也跟着创建。对象消失,成员变量也跟着消失; |
静态变量(类变量) | 类内部,static修饰 | 类 | 类被加载,静态变量就有效; |
常量
final修饰,命名:全用大小写,单词之间用下划线
- 字符常量 (1 , 2, 3 , true , false, "hello"等)
- 符号常量(final修饰的)
long 类型的整形常量结尾加 L(大小写都可,一边用大写,易于区分)
long a = 3000000000L;(int类型一般为21亿左右,超过就要用long);
float f1 = 3.14F (浮点常量的类型默认为double,加F表示为float);
java中字符串不是基本类型,而是类
java中的boolean不能用0 1 代替,一般情况下,boolean占4字节和int一样,布尔类型数组时占一个字节
运算和运算符
- 整数: 有long时,结果为long,没有long时,结果是int(包括short,byte)
- 浮点数:有double时,结果为double,两个都是float时,结果才是float
java中char占两个字节,short 占两个字节,int 占4字节
字符串连接符:
如果“ + ”两侧的操作数有一个是String类型字符串,则会自动将另一侧的操作数转化为String,包括数字。
char类型相加 时,自动将结果转化为整型(根据ASCII计算);
char类型与整型运算是,结果也是整型
运算符优先级
- 逻辑运算符:非 > 与 > 或
数据类型自动转化
容量小的(表示范围)可以自动转化为容量大的, int -> float, long -> float, byte -> short - > int;
特例,整型常量默认是 int, 但可以自动转化为 byte,short,char 只要不超过表示范围
数据类型强制转化
控制语句
switch
switch中表达式的值,是int(byte、short、char也可,long 不行)、枚举,字符串。
方法 Method
方法的重载 overload
只有形参列表不一样才构成重载
参数传值机制
所有的参数都是值传递(基本类型,引用类型)*重要面试点
面对对象(Object Oriented Programming)
field 表格 列
field 类 属性
类
属性(field)
方法(Method)
构造器(constructo)
构造器4个要点:
- 构造器通过new关键字调用!!
- 构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类), 不能在构造器里使用return返回某个值。
- 如果我们没有定义构造器,则编译器会自动定义一个无参的道万法。如果已经定义则编译器不会自动添加!
- 构造器的方法名必须和类名一致!
创建对象的四步:
- 分配对象空间,并将对象成员变量初始化为0或空
- 执行属性值的显式初始化
- 执行构造方法
- 返回对象的地址给相关的变量
每个源文件必须有且只有一个public 类, 并且与源文件保持一致
一个Java文件可以同时定义多个类
简单内存分析
Java虚拟机
栈 stack :存放方法
- 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
- JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
- 栈属于线程私有,不能实现线程间的共享!
- 栈的存储特性是“先进后出,后进先出”
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
堆 heap : 存放对象
- 堆用于存储创建好的对象和数组(数组也是对象)
- JVM只有一个堆,被所有线程共享
- 堆是一个不连续的内存空间,分配灵活,速度慢!
- 堆被所有的线程所共享,在堆上的区域,会被垃圾回收器做进一步划分,例如新生代、老年代的划分。
方法区 Method area(也是堆): 存放类
-
方法区是JAVA虚拟机规范,可以有不同的实现。
-
- JDK7以前是“永久代”
- JDK7部分去除“永久代”,静态变量、字符串常量池都挪到了堆内存中
- JDK8是“元数据空间”和堆结合起来。
-
JVM只有一个方法区,被所有线程共享!
-
方法区实际也是堆,只是用于存储类、常量相关的信息!
-
用来存放程序中永远是不变或唯一的内容。(类信息【Class对象,反射机制中会
-
重点讲授】、静态变量、字符串常量等)
-
常量池主要存放常量:如文本字符串、final常量值。
垃圾回收算法
1.引用计数算法
堆中的每个对象都对应一个引用计数器当有引用指向这个对象时,引用计数器加1,
而当指向该对象的引用失效时(引用变为null),引用计数器减1,
最后如果该对象的引用计算器的值为0时,则Java垃圾回收器会认为该对象是无用对象并对其进行回收。
优点是算法简单,缺点是“循环引用的无用对象”无法别识别。
2.引用可达法(根搜索算法)
程序把所有的引用关系看作一张图
从一个节点GC ROO开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,
当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。
3.通用的分代垃圾回收机制
内存泄漏
指堆内存由于某种原因程序未释放,造成内存浪费,导致运行速度减慢甚至系统崩溃等。
- 创建大量无用对象
比如:大量拼接字符串时,使用了String而不是StringBuilder。
- 静态集合类的使用
像HashMap、Vector、List等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所有的对象也不能被释放。
- 各种连接对象(IO流对象、数据库连接对象、网络连接对象)未关闭
IO流对象、数据库连接对象、网络连接对象等连接对象属于物理连接,和硬盘或者网络连接,不使用的时候一定要关闭。
- 监听器的使用不当
释放对象时,没有删除相应的监听器
其他要点
1.程序员无权调用垃圾回收器。
2.程序员可以调用System.gc(),该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full GC,成本高,影响系统性能。
\3. Object对象的finalize方法,是Java提供给程序员用来释放对象或资源的方法,但是尽量少用
几个重要的关键字
this 的本质和用法
- 普通方法中,this总是指向调用该方法的对象。
- 构造方法中,this总是指向正要初始化的对象。
- this()调用重载的构造方法,避免相同的初始化代码。但只能在构造方法中用,并且必须位于构造方法的第一句。
- this 不能用于static方法中。
static 关键字
static方法和属性都属于类
静态变量/静态方法生命周期和类相同,在整个程序执行期间都有效。它有如下特点:
- 为该类的公用变量,属于类,被该类的所有实例共享,在类载入时被初始化。
- static 成员变量只有一份。
- 一般用“类名.类属性/方法”来调用。
- 在static方法中不可直接访问非static的成员。
静态初始化块
- 构造方法用于对象的普通属性初始化。
- 静态初始化块,用于类的初始化操作,初始化静态属性。
- 在静态初始化块中不能直接访问非static成员。
- 静态初始化块在类加载的时候就会被执行。
注意事项
静态初始化块执行顺序(学完继承再看这里):
上溯到Object类,先执行Object 的静态初始化块,再向下执行子类的静态初始化块,直到当前类的静态初始化块为止。
构造方法执行顺序和上面顺序一样!!!
包机制(package, import)
package的使用有两个要点:
- 通常是类的第一句非注释性语句, 说明该类所在的包
- 包名:域名倒着写即可,再加上模块名,便于内部管理类。
- 同一个包下类名不能相同。
- 调用不同包下的类,需要导入(import)该类,或者该类所在的整个包。
- 不导入时需要写完整的报名+类名
- 调用不同包下同名的类时,只能写完整的包名 + 类名
JDK中的主要包 | |
---|---|
java中的常用包 | 说明 |
java.lang(不需要导入) | 包含一些Java语言的核心类,如String、Math、Integer、System和Thread。 |
java.awt | 包含了构成抽象窗口工具集 (abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。 |
java.net | 包含执行与网络相关的操作的类。 |
java.io | 包含能提供多种输入/输出功能的类。 |
java.util | 包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。 |
导入类(import)(包)
注意要点
- Java 会默认导入java.lang 包下所有的类,因此这些类我们可以直接使用。
- 如果导入两个同名的类,只能用包名+类名来显示调用相关类: java.util.Date date = new java.util.Date();
// 表示导入java.util包下的所有类,会降低编译速度,但不会降低运行速度
// 实际编译时依然会根据使用的类来编译,并不会编译所有.
import java.util.*
静态导入(类)
- 静态导入(static import):
其作用是用于导入指定类的静态属性和静态方法,这样我们可以直接使用静态属性和静态方法。
// 示例
import java.lang.Math.*;
import java.lang.Math.PI;
继承 (extends)
面对对象的三大特征:
继承, 封装, 多态
定义类时如果没写继承, 会默认继承 Object 类, 即所有的类都会继承Object类
判断继承关系
instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。比如:
继承使用要点
- 父类也称作超类、基类。
- 子类:派生类等。
- Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
- Java中类没有多继承,接口有多继承。
- 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
- 如果定义一个类时,没有调用extends,则它的父类是: java.lang.Object。
方法重写 (override)
子类重写父类的方法,可以用自身行为替换父类行为。重写是实现多态的必要条件。方法重写需要符合下面的三个要点:
- “= =”:方法名、形参列表相同。
- “≤”︰返回值类型和声明异常类型,子类小于等于父类。(返回值类型相同, 或者是子类);
- “>”:访问权限,子类大于等于父类。
final关键字
final关键字的作用:
- 修饰变量:被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
final int MAX_SPEED = 120;
- 修饰方法:该方法不可被子类重写。但是可以被重载!
final void study(){}
- 修饰类:修饰的类不能被继承。比如: Math、String等。
final class A {}
组合(component)
- 将父类的对象作为子类的属性
在子类(概念上的)中创建一个父类的对象,这样就可以使用父类的属性和方法了。
组合比较灵活,继承只有一个父类,但是组合可以有多个类的属性。
具体使用时应考虑类与类之间的逻辑关系。
Object类
所有的类都是Object类的子类
toString() 方法
默认返回完整的类名+对象的地址。
在下列代码中,默认调用 toString 方法。
public class TestObject extends Object{
public static void main(String[] args) {
TestObject t1 = new TestObject();
System.out.println(t1);
}
}
equals() 方法
equals() 和 “==” :
“==” 用于比较双方是否相等,基本类型比较值,引用类型比较地址
equals() 用于比较对象是否相等,默认是比较对象的hashcode,可以重写为比较相同对象的特定属性。
super关键字
- super“可以看做”是直接父类对象的引用。可通过super来访问父类中被子类覆盖的方法或属性。
- 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
- 在一个类中,若是构造方法的第一行没有调用super(…)或者this(…); 那么Java默认都会调用super(),含义是调用父类的无参数构造方法。
- super 只能在子类内部调用
继承树追溯
-
属性/方法查找顺序:(比如:查找变量h)
-
- 查找当前类中有没有属性h
- 依次上溯每个父类,查看每个父类中是否有h,直到Object如果没找到,则出现编译错误。
- 上面步骤,只要找到h变量,则这个过程终止。
-
·构造方法调用顺序:
-
- 构造方法第一句总是: super(…)来调用父类对应的构造方法。所以,流程就是:先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
注:静态初始化块调用顺序,与构造方法调用顺序一样,不再重复。
封装 (encapsulation)
封装的理念: 高内聚,低耦合
访问权限修饰符
修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private(私有) | ✔ | |||
default(缺省, 不写) | ✔ | ✔ | ||
protected(受保护的) | ✔ | ✔ | ✔ | |
public(公有) | ✔ | ✔ | ✔ | ✔ |
【注】关于protected的两个细节:
- 若父类和子类在同一个包中,子类可访问父类的 protected成员,也可访问父类对象的protected成员。
- 若子类和父类不在同一个包中,子类可访问父类的protected成员,不能访问父类对象的protected成员。
- 在同一个包中,没有继承关系,可以访问父类对象的protected成员。
类成员访问权限控制
public | 全局友好 |
---|---|
protected | 父子友好,包内友好 |
default | 包内友好 |
private | 类内友好 |
开发中封装的简单规则:
-
属性─般使用private访问权限。
-
- 属性私有后,提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是 is开头!)。
-
方法:一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
多态 (polymorphism)
多态指的是同一个方法调用,由于对象不同可能会有不同的行为。
多态的要点:
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存在要有3个必要条件: 继承,方法重写,父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
父类引用可以访问子类重写的方法,但是不能访问子类独有的方法。
此时,父类是编译类型,子类是运行时类型。
对象的转型(casting)
- 父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
- 向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型
一个父类有很多子类时,当父类的引用指向子类的对象时,可以向下转型为该子类的对象,如果转型为其他子类的对象,就会出错。
// 类型转换(伪代码)
class Animal {}
class Dogs extends Animal{}
class Cat extends Animal{}
public viod main() {
// 父类引用指向子类对象(多态)
Animal animal = new Dogs(); // 向上转型,此时可以调用子类继承的方法,
// 但不能调用子类独有的方法
Dog dog = (Dog)animal; // 向下转型,可以调用子类独有的方法。
Cat cat = (Cat)animal; // 类型转化错误:ClassCastException
}
抽象类
抽象方法
- 使用abstract修饰的方法,没有方法体,只有声明。
- 定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类
- 包含抽象方法的类就是抽象类。
- 通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
抽象类的使用要点:
- 有抽象方法的类只能定义成抽象类
- 抽象类不能实例化,即不能用new来实例化抽象类。
- 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new 实例,只能用来被子类调用。
- 抽象类只能用来被继承。
- 抽象方法必须被子类实现。
接口(interface)
接口 interface, 实现 implements
- 接口全面的专业的实现了: 规范和具体实现的分离。
- 接口和实现类不是父子关系, 是实现规则关系.
- 接口也是类, 一种特殊的类
定义和使用接口
声明格式:
[访问修饰符] interface 接口名 [extends 父接口1, 父接口2] {
常量定义;
方法定义;
}
定义接口的详细说明:
- 访问修饰符: 只能是public或默认。
- 接口名: 和类名采用相同命名机制。
- extends: 接口可以多继承。
- 常量: 接口中的属性只能是常量,总是: public static final修饰。不写也是。
- 方法: 接口中的方法只能是: public abstract。省略的话,也是public abstract.
要点
- 子类通过implements来实现接口中的规范。接口不能创建实例,但是可用于声明引用变量类型。
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
- JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
- JDK1.8(含8)后,接口中包含普通的静态方法、默认方法。
接口默认方法(扩展方法)
默认方法用default修饰
接口可以提供默认方法的实现,所以实现类都可以得到默认方法,默认方法可以被重写,也可以不重写
接口静态方法
接口中可以直接定义静态方法, 这种静态方法从属于接口, 可以直接使用接口名来调用. 不可以通过子类调用
子类中如果定义了相同名字的静态方法, 那就是完全不同的方法.
静态方法不能调用普通方法, 普通方法属于对象,
普通方法可以调用静态方法
接口的多继承(multiple inheritance)
接口可以多继承
interface A {
void testA();
}
interface B {
void testB();
}
// 接口的多继承
interface C extends A, B{
void testC();
}
// 实现接口和父级接口的抽象方法.
class CImpl implements C {
void testA() {
}
void testB() {
}
void testC() {
}
}
子类实现接口时必须实现接口和父级接口的抽象方法。
内部类 (inner class)
我们把一个类放在另一个类的内部定义,称为内部类(inner class).
内部类分类 | 成员内部类 | 非静态内部类 |
---|---|---|
静态内部类 | ||
匿名内部类 | 只使用一次的类 | |
局部内部类 | 定义在方法内部, 仅作用于该方法 |
内部类的两个要点
- 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
- 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。但外部类不能访问内部类的内部属性。
注意
内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为 Outer的外部类和其内部定义的名为lnner的内部类。编译完成后会出现 Outer.class和 Outersinner.class 两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。
非静态内部类
(外部类里使用非静态内部类和平时使用其他类没什么不同)
-
1.非静态内部类对象必须寄存在一个外部类对象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
-
非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
-
非静态内部类不能有静态方法、静态属性和静态初始化块
-
成员变量访问要点:
-
- 内部类属性: this.变量名
- 外部类属性:外部类名.this.变量名
内部类的访问
1.外部类中定义内部类: new Inner0。
2.外部类以外的地方使用非静态内部类
Outer.nner varname = new Outer().new Inner().
内部类对象依存于外部类对象, 现有外部类对象, 再有内部类对象,
静态内部类
定义方式
static class ClassName {
// 类体
}
使用要点:
- 静态内部类可以访问外部类的静态成员, 不能访问外部类的普通成员;
- 静态内部类可以看作外部类的一个静态成员;
匿名内部类(anonymous inner class)
只使用一次的类, 开发中常见.
// 语法
new 父类构造器(实参列表)\ 实现接口 () {
// 类体
}
// 匿名内部类的使用
public class TestAnonymousInnerClass {
public void test1 (A a) {
a.run();
}
public static void main(String[] args) {
TestAnonymousInnerClass t = new TestAnonymousInnerClass();
t.test1(new A() {
@Override
public void run() {
System.out.println("TestAnonymousInnerClass.run");
}
});
}
}
interface A {
void run();
}
局部内部类(local inner class )
定义在方法内部的, 作用域仅限于本方法, 称为局部内部类.
在开发中应用很少.
// 局部内部类的使用
public class TestLocalInnerClass {
public void show() {
class Inner3 {
public void fun() {
System.out.println("hello world");
}
}
new Inner3().fun();
}
public static void main(String[] args) {
new TestLocalInnerClass().show();
}
}
面向对象大总结
数组
数组也是对象
数组的概念
数组的定义
数组是相同数据类型的有序集合。
数组的四个特点
- 数组的长度是确定的。数组一旦被创建, 大小就是不可变的。但是数组的引用可以改变。
- 元素类型必须相同
- 数组类型可以是基本类型和引用类型
- 数组变量是引用类型(可变)。数组也是对象, 数组的元素相等与对象的属性。
数组的初始化
- 静态初始化
public class Test {
public static void main (String Args[]) {
int[] a = new int[10];
a[0] = 1;
a[1] = 2;
ClassDemo[] c= new ClassDemo[10];
c[0] = new ClassDemo();
c[1] = new ClassDemo();
}
}
- 动态初始化
public class Test {
public static void main (String Args[]) {
int[] a = new int[10];
a[0] = 1;
a[1] = 2;
ClassDemo[] c= new ClassDemo[10];
c[0] = new ClassDemo();
c[1] = new ClassDemo();
}
}
- 默认初始化
数组是对象, 它的元素相当于对象的属性, 每个元素也按照属性的方式被默认初始化。
for-each 循环
专门用于读取数组或容器中的所有元素。
public static void main(String[] args) {
String [] ss = {"aa", "bb", "cc"};
for (String temp:ss) {
System.out.println(temp);
}
}
- for - each 增强for循环在遍历时只能读取, 不能修改.
- for - each 增强for循环, 不涉及有关索引的操作。
数组的拷贝
方法**: System.arraycopy(object src, int srcpos, object dest, int destpos, int lenght)**
src : 源数组
srcpos : 源数组中的起始位置
dest : 目标数组的起始位置.
length : 要复制的长度
java.util.Arrays类
包含排序、查找、填充、打印内容等常见的数组操作。
import java.util.Arrays;
public class Test01 {
public static void main(String[] args) {
// 静态初始化
int[] a = {1, 2, 232, 23, 543, 12, 59};
// 排序
Arrays.sort(a);
// 二分法查找, 数组必须是有序的
Arrays.binarySearch(a, 12);
// 填充数组
Arrays.fill(a, 2, 4, 100);
// 输出打印数组
System.out.println(Arrays.toString(a));
}
}
/*
输出结果 :
[1, 2, 100, 100, 59, 232, 543]
*/
多维数组
Java中多维数组的声明和初始化应按从低维到高维(从左到右)的顺序进行。
低维的长度没有定义, 高维的长度就无法确定。
int[][] a = new int[3][]; // 正确
int[][] a1 = new int[][4]; // 错误
int[][] b = { // 正确
{1, 3, 5, 7},
{4, 6, 8, 10, 12},
{2, 5, 8, 11, 14}
};
int[][] c = new int [3][];
//c[0] = {1, 3}; // 错误, 没有声明类型就初始化.
c[0] = new int[] {1, 3}; // 正确
Object 数组存储表格
1001 | 张三 | 27 | java工程师 |
---|---|---|---|
1002 | 李四 | 28 | 网络爬虫工程师 |
1003 | 王五 | 32 | 算法工程师 |
// Object 数组存储表格数据
Object[][] example = new Object[3][];
example[0] = new Object[] {1001, "张三", 27, "java工程师"};
example[1] = new Object[] {1002, "李四", 28, "网络爬虫工程师"};
example[2] = new Object[] {1003, "王五", 32, "算法工程师"};
for (Object[] each:example) {
System.out.println(Arrays.toString(each));
}
Object 数组的元素是对象, 整型 int 本质上是基础数据类型, 不是对象, 但是Java会自动装箱(包装类).
javabean 加数组 存储表格
javabean的三个要点
- 这个Java类必须具有一个无参的构造函数
- 属性必须私有化。
- 私有化的属性必须通过public类型的方法暴露给其它程序,并且方法的命名也必须遵守一定的命名规范。
// 测试 javabean 加数组 存储表格
public class Test02 {
public static void main(String[] args) {
Emp[] emps = {
new Emp(1001, "张三", 27, "Java工程师"),
new Emp(1002, "李四", 28, "网络爬虫工程师"),
new Emp(1003, "王五", 32, "算法工程师")
};
for (Emp each :
emps) {
System.out.println(each.toString());
}
}
}
class Emp {
private int id;
private String name;
private int age;
private String job;
public Emp() {
}
public Emp(int id, String name, int age, String job) {
this.id = id;
this.name = name;
this.age = age;
this.job = job;
}
@Override
public String toString() {
return "[" + id +
", " + name +
", " + age +
", " + job +
']';
}
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 getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
}
Comparable接口
-
用于定义比较的策略
-
Java中排序算法的底层也会依赖Comparable接口
-
Comparable接口只有一个方法:
-
- public int compareTo(Object obj)
- obj 为要比较的对象
-
将当前对象和obj进行比较,
-
-
如果:
-
- 大于返回 1
- 小于返回 -1
- 相等返回 0
-
异常机制 (Exception)
异常机制的核心类
Java是采用面向对象的方式来处理异常的.
处理过程:
- 抛出异常: 在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一
对象,停止当前执行路径,并把异常对象提交给JRE。
- 捕获异常: JRE 得到该异常后,寻找相应的代码来处理该异常。JRE 在方法的调用
栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止
异常的分类:
-
Error是程序无法处理的错误
-
Exception是升序本身可以处理的异常
-
-
RuntimeException (运行时异常) 可以通过编译。不需要try-catch
-
- NullPointerException
- ArithmeticException
- NumberFormatException
-
CheckedException (已检查异常) 不能通过编译。需要try-catch
-
- SQLException
- ClassNotFoundException
-
异常的处理方式之一:捕获异常
使用 try-catch-finally 语句, 捕获异常
try {
// 语句 1;
// 语句 2;
// 语句 3;
} catch (Exception1 e) { // 如果有多类异常, 父类要放在子类上面
e.printStackTrace();
}catch (Exception2 e) {
e.printStackTrace();
}catch (Exception3 e) {
e.printStackTrace();
} finally { // 无论发生异常与否, 都会执行的语句
// 语句 4; // 可用于关闭资源.
// 语句 5;
}
实际应用
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("d:/a1.txt");
char c0 = (char)reader.read();
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
try :
- try 语句指定了一段代码,该段代码就是异常捕获并处理的范围。在执行过程中,当任
意一条语句产生异常时,就会跳过该条语句中后面的代码。代码中可能会产生并抛出一种或
几种类型的异常对象,它后面的 catch 语句要分别对这些异常做相应的处理。
- 一个 try 语句必须带有至少一个 catch 语句块或一个 finally 语句块。
当异常处理的代码执行结束后, 不会回到 try 语句去执行尚未执行的代码。
catch:
- 每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类
型的异常对象。
-
catch 捕获异常时的捕获顺序
-
- 如果异常类之间有继承关系,先捕获子类异常再捕获父类异常
finally:
- 不管是否发生异常,都必须执行。
- 通常在finally中关闭已经打开的资源,比如:关闭文件流、释放数据库连接等。
异常处理方式之二:声明异常
- CheckedException产生时,不一定立刻处理它,可以把异常 throws 抛出,由调用者处理。
- 一个方法抛出多个CheckedException时, 就必须在声明处列出所有的异常。
- 如果子类异常和父类异常同时存在时, 只写父类即可。
public static void main(String[] args) throws IOException {
FileReader reader = null;
try {
reader = new FileReader("d:/a.txt");
char c0 = (char)reader.read();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
try-with-resource
- try-with-resource 自动关闭 Closable 接口的资源
- JDK7 之后,新增了“try-with-resource"它可以自动关闭实现了AutoClosable 接口的类,实现类需要实现 close0方法。”try-with-resources声明”,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编译时仍然会进行转化为 try-catch-finally 语句。
public static void main(String[] args) {
try(FileReader reader = new FileReader("d:/a.txt")) {
char c = (char)reader.read();
}catch (Exception e) {
e.printStackTrace();
}
}
自定义异常
- 在程序中,可能会遇到JDK 提供的任何标准异常类都无法充分描述清楚我们想要表达的问题,这种情况下可以创建自己的异常类,即自定义异常类。
- 自定义异常类只需从 Exception 类或者它的子类派生一个子类即可。
- 自定义异常类如果继承 Exception 类,则为 CheckedException 异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException 类
- 习惯上,自定义异常类应该包含 2 个构造器:一个是默认的构造器,另一个是带有详细信息的构造器
包装类(Wrapper Class)
包装类基本知识
Java 是面向对象的语言,但并不是“纯面向对象”的,因为我们经常用到的基本数据类型就不是对象。但是我们在实际应用中经常需要将基本数据转化成对象,以便于操作。比如:将基本数据类型存储到 Objectl ]数组或集合中的操作等等。
为了解决这个不足,Java 在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
包装类位于java.lang 包,八种包装类和基本数据类型的对应关系
基本数据类型对应包装类 | |
---|---|
基本数据类型 | 包装类 |
byte | Byte |
boolean | Boolean |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
Number类
八中数据类型中有六种是数字类型,这六种数字类型的基本类型的包装类都是Number类的子类。
Number类是抽象类,因此它的抽象方法,所有的子类都要实现。Number类提供了供所有子类进行类型转换的方法。
public static void main(String[] args) {
Integer i = new Integer(10); // 不推荐使用
Integer j = Integer.valueOf(20); // 官方推荐
// 把包装类转成基本数据类型
int a = j; // 可以, 但是不推荐
int b = j.intValue(); // 正确
// 把字符串转成数字
Integer m = Integer.valueOf("456"); // 正确
int n = Integer.valueOf("456");
// valueof 方法返回的是Integer对象
// parseInt 方法返回的是int整型
// 但是int 和 Integer 之间会自动转换, 所有混用也没关系, 但是不推荐.
int s = Integer.parseInt("123"); // 正确
Integer t = Integer.parseInt("123");
}
自动装箱和拆箱
将基本数据类型和包装类自动转换
Integer缓存问题 (IntegerCache)
IntegerCache类是Integer类的一个内部类, 该类中存在一个 Integer[] cache 数组, 存放"-128 ~ 127"的Integer对象, 以提高效率.
在创建value范围在 “-128 ~ 127” 的 Interger 对象时, 会直接返回cache数组中对应的对象.
即, value在该范围内的Integer对象, 如果value相等, 则是同一个对象.
Java常见类
String类
String类详解
- String 类又称作不可变字符序列。
- String 位于java.lang 包中,Java 程序默认导入java.lang 包下的所有类
- Java 字符串就是 Unicode 字符序列,例如字符串“Java”就是4个 Unicode 字符组成的。
- Java 没有内置的字符串类型,而是在标准Java 类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是 String 类的一个实例。
// String 的初始化
String s0 = null; //字符串为空
String s1 = "";//空字符串, 两者含义不同.
String s2 = "java";
String s3 = new String("java");
常量池原理
String g1 = "北京尚学堂";
String g2 = "北京尚学堂";
String g3 = new String("北京尚学堂");
String 类的常用方法
方法 | 用途 |
---|---|
char charAt(int index) | 返回字符串中下标为 index 的字符 |
boolean equals(String other) | 判断字符串是否相等; 相等返回true, 否则返回false |
boolean equalsIgnoreCase(String other) | 判断是否相等(忽略大小写) |
int indexOf(String str) | 返回第一个字符串str的下标, 否则返回-1 |
int lastIndexOf(String str) | 返回最后一个字符串str的下标, 否则返回-1 |
int length() | 返回字符串长度 |
String replace(char oldChar, char new) | 返回一个新串, 以newChar替换原字符串中的所有oldChar, |
String replaceAll(String regex, String replacement) | 返回一个新串, 以 replacement 替换原字符串中所有满足表达式regex的子串 |
boolean startsWith(String prefix) | 判断字符串是否以prefix开始, 是返回true, 否则返回false |
boolean endsWith(String prefix) | 判断字符串是否以prefix结束, 是返回true, 否则返回false |
String subString(int beginIndex) | 返回一个新串, 包含原字符串从下标为beginIndex到末尾 |
String subString(int beginIndex, int endIndex) | 返回一个新串, 包含原字符串从下标为beginIndex(包含)到下标为endIndex(不包含)的子串 |
String toLowerCase() | 返回一个新串, 将原串所有字符小写 |
String toUpperCase() | 返回一个新串, 将原串所有字符大写 |
String trim() | 返回一个新串, 去除原串头尾的所有空格 |
str1 == str2 是判断两个字符串的对象是否相同. str1.equals(str2)判断字符串本身是否一致.
StringBuffer类 和 StringBuilder类
- StringBuilder 线程不安全, 不做线程检查, 效率较高, 常用
- StringBuffer 线程安全, 做线程同步检查, 效率较低
StringBuilder 和StringBuffer 的方法大多都是修改原对象, 而后返回原对象, 因此可以写方法链;
时间相关类
在计算机的世界里, 我们把1970年1月1日00:00:00 定为基准时间, 每个度量单位毫秒.
Date时间类(java.util.Date)
DateFormat类 和 SimpleDateFormat类
DataFormat类是一个抽象类, SimpleDateFormat是子类.
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDateFormat {
public static void main(String[] args) throws ParseException {
// 将字符串按特定模式转换成Data对象
DateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String str = "2049-10-1 10:10:10";
Date guoqing = format.parse(str);
System.out.println(guoqing.getTime());
System.out.println(guoqing);
// 将Date对象转换为字符串, 按特定模式
DateFormat format2 = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒");
Date date2 = new Date();
String date2str = format2.format(date2);
System.out.println(date2str);
}
}
Calendar 日历类
Calendar类是一个抽象类, 提供了关于日期的展示和计算功能.
GregorianCalendar 是Calendar的子类, 表示公历.
💡值得注意的是, 该类中, 月份的表示从 0 开始, 即 January对应的是0, February对应的是1;
星期的表示为, Sunday是一周里的第一天, 对应 1 , Saturday 对应 7;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class TestCalendar {
public static void main(String[] args) {
// 得到相关日期元素
GregorianCalendar calendar = new GregorianCalendar
(2049, 9, 1, 22, 10, 50);
calendar.setTime(new Date());
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1;
int day_of_month = calendar.get(Calendar.DAY_OF_MONTH);
int date = calendar.get(Calendar.DATE);
int day = calendar.get(Calendar.DAY_OF_WEEK) - 1;
System.out.println("====================");
System.out.println(year);
System.out.println(month);
System.out.println(day_of_month);
System.out.println(date);
System.out.println(day);
System.out.println("====================");
System.out.println(calendar);
System.out.println(getCalendar(calendar));
// 设置日期
calendar.set(Calendar.YEAR, 2099);
calendar.set(Calendar.MONTH, Calendar.MARCH);
calendar.set(Calendar.DAY_OF_WEEK, 10);
calendar.set(Calendar.DATE, 1);
calendar.set(Calendar.HOUR_OF_DAY, 10);
calendar.set(Calendar.MINUTE, 10);
calendar.set(Calendar.SECOND, 10);
System.out.println(getCalendar(calendar));
// 日期计算
calendar.add(Calendar.MONTH, 10);
calendar.add(Calendar.DATE, 11);
System.out.println(getCalendar(calendar));
}
static String getCalendar (Calendar calendar) {
return "" +
calendar.get(Calendar.YEAR) + "年" +
(calendar.get(Calendar.MONTH)+1) + "月" +
calendar.get(Calendar.DAY_OF_MONTH) + "日, 星期" +
(calendar.get(Calendar.DAY_OF_WEEK)-1) + " " +
calendar.get(Calendar.HOUR) + ":" +
calendar.get(Calendar.MINUTE) + ":" +
calendar.get(Calendar.SECOND);
}
}
设置日期时, 当设置的date和day_of_week冲突时, 会选择后写的那个, 并且自动计算出前者,
创建Calendar对象时, 不传参会自动使用当前系统时间.
数学相关类(Math, Random)
File 类
可以访问文件, 硬盘系统
File类的基本用法
java.io.file类: 代表文件和目录, 用于: 读取文件, 创建文件, 删除文件, 修改文件
File类常见构造方法: public File(String pathname)
以pathname为路径创建File对象, 如果pathname是相对路径, 则 默认的当前路径在系统属性 “user.dir” (当前项目所在的路径)中存储.
创建File对象, 创建文件
File类创建文件夹或目录的方法
- createNewFile() 创建新的File
- delete() 删除File对应的文件
- mkdir() 创建一个目录, 中间某个目录缺失, 则创建失败
- mkdirs() 创建 多层 目录, 中间某个目录缺失, 则创建失败
import java.io.File;
import java.io.IOException;
public class TestFile1 {
public static void main(String[] args) throws IOException {
System.out.println(System.getProperty("user.dir"));
File f = new File("a.txt"); // 相对路径, 默认放到 user.dir目录下, 即项目所在目录
f.createNewFile(); // 创建文件
File f2 = new File("d:/b.txt"); // 绝对路径
f2.createNewFile();
}
}
File类访问属性的方法 | |
---|---|
方法 | 说明 |
public boolean exists() | 判断File是否存在 |
public boolean isDirectory() | 判断File是否是目录(文件夹) |
public boolean isFile() | 判断File是是否是文件 |
public long lastModified | 返回File最后修改的时间 |
public long length() | 返回File大小 |
public String getName() | 返回文件名 |
public String getPath() | 返回文件的目录路径 |
枚举类型
枚举类型的定义包括枚举声明和枚举体, 格式如下
enum 枚举名 {
枚举体(商量列表);
}
所有的枚举类型隐性地继承自 java.lang.Enum。枚举实质上还是类!
而每个被枚举的成员实质就是一个枚举类型的实例,他们默认都是 public static final 修饰的。
可以直接通过枚举类型名使用它们。
- 当你需要定义一组常量时, 可以使用枚举类型
- 尽量不要使用枚举的高级特性, 事实上, 高级特性都可以使用普通类来实现, 没有必要增加复杂性
public class TestEnum {
public static void main(String[] args) {
// 遍历枚举类型
for (Season s :
Season.values()) {
System.out.println(s);
}
// 枚举类型结合switch语句
int a = new Random().nextInt(4);
Season s = Season.values()[a];
switch (s) {
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}
容器和泛型
泛型(generic)
泛型的定义
泛型是JDK1.5 以后增加的,它可以帮助我们建立类型安全的集合。
泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参
数传入。 我们可以把“泛型”理解为数据类型的一个占位符(类似: 形式参数),即告诉编
译器,在调用泛型时必须传入实际类型。这种参数类型可以用在类、接口和方法中,分别被
称为泛型类、泛型接口、泛型方法。
- 把类型当做参数一样传递
- <数据类型>只能是引用类型
类型擦除
编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。
泛型是一个编译的概念,编译后生成的字节码 class 文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成 obiect,运行时虚拟机并不知道泛型。
使用泛型的好处
- 代码可读性更好, 书写更方便
- 程序更安全
定义泛型
定义泛型的字符可以是任何标识符, 一般采用: E, T, K, V, N, ?
E | Element | 在容器中使用, 表示容器中的元素 |
---|---|---|
T | Type | 表示普通的Java类 |
K | Key | 表示键, 例如Map中的Key |
V | Value | 表示值 |
N | Number | 表示数字类型 |
? | 表示不确定的类型 |
泛型类
public class Test { public void main(String args[]) { Mygeneric mygeneric = new MyGeneric<>(); } } class Mygeneric { }
泛型类在声明时要在类的旁边, 使用<>声明泛型, 在使用泛型类创建实例对象时, 要给出实际的类型.
泛型接口
使用泛型的接口在声明时和泛型类相同,
在实现类实现接口时, 可以有两种方法,
可以直接明确泛型的类型
也可以先不明确给定类型, 等该类在实例化的时候在确定.
在引用泛型接口的实现类实例化时, 可以使用实现类名来声明对象的类型, 也可以直接使用接口名来声明.
如果实现类在定义时已经给定了泛型的具体类型,那么, 在使用类名声明对象类型时, 就不用考虑泛型了.(因为泛型的类型已经确定), 但是在使用接口名声明对象类型时, 还是要给出泛型的类型(实现类定义的类型).
总之, 在创建对象时, 声明部分有泛型, 就要给定具体的类型.
public class TestMyGeneric {
public static void main(String[] args) {
// 在创建对象时, 如果该类已经给出具体类型, 就不需要考虑泛型了
MyGenericImpl01 myGeneric1 = new MyGenericImpl01();
myGeneric1.getName("贾国荣");
// 使用接口名声明对象时, 必须给出类型
MyGeneric<String> myGeneric2 = new MyGenericImpl01();
myGeneric2.getName("贾国荣");
// 该类没有给出泛型的具体类型时, 要在创建类时给出
MyGenericImpl02<String> myGeneric = new MyGenericImpl02<>();
myGeneric.getName("贾国荣");
}
}
interface MyGeneric <T> {
T getName (T name);
}
// 在实现类的声明时就确定泛型的具体类型
class MyGenericImpl01 implements MyGeneric<String> {
@Override
public String getName(String name) {
return null;
}
}
// 没有确定具体类型
class MyGenericImpl02<T> implements MyGeneric<T> {
@Override
public T getName(T name) {
return null;
}
}
泛型方法
泛型类中所定义的泛型,在方法中也可以使用。当方法不实用泛型, 而是需要仅仅在某一个方法
上使用泛型时,这时候可以使用泛型方法。
泛型方法是指将方法的参数类型定义成泛型,以便在调用时接收不同类型的参数。类型参数可以有多个,用逗号隔开,如:。定义时,类型参数一般放到返回值前面。
调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型来。
非静态泛型方法
public class TestMethodGeneric {
public static void main(String[] args) {
MethodGeneric methodGeneric = new MethodGeneric();
methodGeneric.getName("贾国荣");
}
}
class MethodGeneric {
// 泛型在访问修饰符后, 在返回值之前
public <T> void getName(T name) {
return;
}
}
静态泛型方法
在静态方法中, 静态方法无法使用类中定义的泛型. 如果静态方法要使用泛型, 就必须在方法上定义.
public static <泛型表示符> void getName (泛型表示符 name) {
}
------------------------
public static <泛型表示符> 泛型表示符 getName (泛型表示符 name) {
}
------------------------
// 使用可变参数的泛型方法
public <泛型表示符> void method (泛型表示符... arr) {
for (泛型表示符 each : arr) {
System.out.println(each);
}
}
通配符和上下限定
无界通配符
“?” 表示类型通配符, 用于替代具体的类型. 它只能在"<>" 中使用, 可以解决具体类型不确定的问题.
这种表示任意类型的通配符为 “无界通配符”.
“?” 是类型实参, 而不是类型形参
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。重要说三遍!此处’?’是类型实参,而不是类型形参 ! 此处’?’是类型实参,而不是类型形参 !再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
原文链接:https://blog.csdn.net/qq_40657585/article/details/99983426
————————————————
语法结构:
public void showFlag (Generic<?> generic) {
}
代码实例:
public class Test04 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic01 = new Generic<>();
generic01.setFlag(10);
showMsg.showFlag(generic01);
Generic<Number> generic02 = new Generic<>();
generic02.setFlag(20);
showMsg.showFlag(generic02);
Generic<String> generic03 = new Generic<>();
generic03.setFlag("30");
showMsg.showFlag(generic03);
}
}
class ShowMsg {
public void showFlag (Generic<?> generic) {
System.out.println(generic.getFlag());
}
}
class Generic <T> {
private T flag;
public void setFlag(T flag) {
this.flag = flag;
}
public T getFlag() {
return flag;
}
}
问题: 20行, 除了使用 “?” 通配符之外, 还可以将代码写成
public void showFlag (Generic generic) {
即, 以使用泛型方法的形式, 解决类型不确定的问题,
但是不知道有什么区别
通配符的上限限定
上限限定是表示通配符的类型是T类, 以及T的子类或者T接口以及T接口的子接口.
该方法也适用于泛型的上限限定.
语法结构
// 通配符上限限定
public void showFlag (Generic<? extends Number> generic) {
}
// 泛型的上限限定
class Generic <T extends Number> {
}
通配符的下限限定
下限限定表示通配符的类型是T类以及T类的父类, 或者T接口以及T接口的父接口.
注意: 该方法不适用于泛型类
语法结构
public void showFlag (Generic<? super Integer> generic) {
}
泛型的总结
泛型主要用于编译阶段, 编译后生成的字节码class文件不包含泛型中的类型信息. 类型参数在编译后会被替换成Object类, 运行时虚拟机并不知道泛型, 因此, 使用泛型时, 有以下几种错误.
几种常见错误:
- 基本类型不能用于泛型
Test t; 这种写法是错误的, 我们可以使用对应的包装类; Test t;
- 不能通过类型参数创建对象.
T type = new T(); 运行时, 类型参数会被替换成Object, 无法创建T类型的对象, 容易引起误会.
容器(collection)
容器的结构
单例集合: 将数据一个一个的存储
List接口: 有序, 可重复
set接口: 无序, 不可重复
双例集合: 基于key与value的交够存储数据
Collection接口
Collection接口中的抽象方法
方法 | 说明 |
---|---|
add(Object element) | 增加元素到容器中 |
boolean remove(Object element) | 从容器中移除元素 |
boolean contains(Object element) | 容器中是否包含该元素 |
int size0 | 容器中元素的数量 |
boolean isEmpty0 | 容器是否为空 |
void clear() | 清空容器中所有元素 |
lterator iterator() | 获得迭代器,用于遍历所有元素 |
boolean containsAll(Collection c) | 本容器是否包含c容器中的所有元素 |
boolean addAll(Collection c) | 将容器c中所有元素增加到本容器 |
boolean removeAll(Collection c) | 移除本容器和容器c中都包含的元素 |
boolean retainAll(Collection c) | 取本容器和容器c中都包含的元素,移除非交集元素 |
Object[] toArray() | 转化成 Object 数组 |
List接口
除了Collection接口中的方法, List多了一些跟顺序(索引)有关的方法.
方法 | 说明 |
---|---|
void add (int index, Object element) | 在指定位置插入元素,以前元素全部后移一位 |
Object set (int index,Object element) | 修改指定位置的元素 |
Object get (int index) | 返回指定位置的元素 |
Object remove (int index) | 删除指定位置的元素,后面元素全部前移一位 |
int indexOf (Object o) | 返回第一个匹配元素的索引,如果没有该元素返回-1. |
int lastindexOf (Object o) | 返回最后一个匹配元素的索引,如果没有该元素返回-1. |
ArrayList类
将单例集合转换成数组
转换成 Object[] 数组和指定类型数组
public class TestList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("" + i);
}
System.out.println("原始数组: \n" + list);
Object[] objs = list.toArray();
System.out.println("Object类型数组: \n" + Arrays.toString(objs));
String[] strs = list.toArray(new String[0]);
System.out.println("指定类型数组: \n" + Arrays.toString(strs));
}
}
在转换为指定类型数组时, 如果传入的数组长度小于集合中元素的个数, 那么方法内部会创建一个新的数组, 并返回
如果传入的数组长度大于集合的元素个数, 那么, 会执行arr[size] = null.arr是传入的数组, size是集合元素个数.
容器的并集操作
boolean addAll(Collection c) | 将原容器与指定容器进行并集操作, 默认是添加到原容器的末尾, 指定容器不得为空 |
---|---|
boolean addAll(int index, Collection c) | 从index索引位置开始, 将指定容器中的元素添加到原容器 |
容器的交集操作
boolean retainAll(Collection c) | 只保留原容器中与指定容器的交集部分 |
---|---|
容器的差集操作
boolean addAll(Collection c) | 保留在原容器, 但不在指定容器的元素 |
---|---|
ArrayList类底层源码
底层是一个Object[] 数组, 创建时, 数组为空(延迟初始化), 添加元素后, 数组长度为10,
随后每次超过数组长度都会进行扩容
扩容操作的实现是创建一个更长的新数组, 长度为原来的1.5倍, 然后将原数组的元素拷贝到新数组.
Vector类
Vector底层使用数组实现的, 相关的方法斗加了同步检查(synchronize), 因此"线程安全, 效率低".
Stack容器
ect o) | 返回第一个匹配元素的索引,如果没有该元素返回-1. |
| int lastindexOf (Object o) | 返回最后一个匹配元素的索引,如果没有该元素返回-1. |
ArrayList类
将单例集合转换成数组
转换成 Object[] 数组和指定类型数组
public class TestList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add("" + i);
}
System.out.println("原始数组: \n" + list);
Object[] objs = list.toArray();
System.out.println("Object类型数组: \n" + Arrays.toString(objs));
String[] strs = list.toArray(new String[0]);
System.out.println("指定类型数组: \n" + Arrays.toString(strs));
}
}
在转换为指定类型数组时, 如果传入的数组长度小于集合中元素的个数, 那么方法内部会创建一个新的数组, 并返回
如果传入的数组长度大于集合的元素个数, 那么, 会执行arr[size] = null.arr是传入的数组, size是集合元素个数.
容器的并集操作
boolean addAll(Collection c) | 将原容器与指定容器进行并集操作, 默认是添加到原容器的末尾, 指定容器不得为空 |
---|---|
boolean addAll(int index, Collection c) | 从index索引位置开始, 将指定容器中的元素添加到原容器 |
容器的交集操作
boolean retainAll(Collection c) | 只保留原容器中与指定容器的交集部分 |
---|---|
容器的差集操作
boolean addAll(Collection c) | 保留在原容器, 但不在指定容器的元素 |
---|---|
ArrayList类底层源码
底层是一个Object[] 数组, 创建时, 数组为空(延迟初始化), 添加元素后, 数组长度为10,
随后每次超过数组长度都会进行扩容
扩容操作的实现是创建一个更长的新数组, 长度为原来的1.5倍, 然后将原数组的元素拷贝到新数组.
Vector类
Vector底层使用数组实现的, 相关的方法斗加了同步检查(synchronize), 因此"线程安全, 效率低".
Stack容器
Stack栈容器, 是Vector的一个子类, 实现了一个标准的后进先出(LIFO : last in first out)的栈.