开学就要大四了,校招也要开始了。想想自己的能力,感觉差了太多太多,暑假都不敢出去玩,想着多花点时间复习总结一下以前的知识,找工作时能用的到。
Java是我大一时自学的,现在会写但是很多没用到的概念全给忘了,于是就想复习一下。因为我已经有了一定的基础知识,所以这个笔记并不是一个详尽的Java笔记,而只是记录我所遗忘或需要加强记忆的部分。
基础
发展历史
1995年sun公司推出Java语言,2009年Oracle公司收购sun公司
JDK环境变量配置
JAVA_HOME:配置JDK安装路径
PATH/path:配置JDK命令文件的位置,bin
CLASSPATH:配置类库文件的位置,lib
Java中的注释
Java有三种类型:单行注释、多行注释、文档注释
public class HelloWorld{
/**
* 文档注释
* @author niujiajun
* @version v1.0
*/
public static void main(String[] args) {
//System.out.println("Java"); 单行注释
System.out.println("imooc");
/* 多行注释,以/*开始,以*/结束
* System.out.println("Oracle");
* System.out.println("MySQL");
*/
}
}
文档注释:以/*开始,以/结束
使用文档注释时还可以使用 javadoc 标记,生成更详细的文档信息:
@author 标明开发该类模块的作者
@version 标明该类模块的版本
@see 参考转向,也就是相关主题
@param 对方法中某参数的说明
@return 对方法返回值的说明
@exception 对方法可能抛出的异常进行说明
- 多行注释:以/开始,以/结束
- 单行注释:在行前加//
输出
在System.out.println()输出时,需要比较变量或处理变量只要加括号就可以了,如:
int a=16;
double b=9.5;
System.out.println("a等于b:" + (a == b));
输出结果是
a等于b: false
print和println的区别是后者会换行
输入
输入需要用到java.util.Scanner包
Scanner input = new Scanner(System.in);//建立Scanner对象
int i = input.nextInt();//新建整型变量接收输入的数据
javadoc生成文档
通过javadoc命令从文档注释中提取内容,生成程序的 API 帮助文档
javadoc -d 文件夹名称 java文件
运算符
算术运算符++,--
++自增1,--自减1,自增和自减运算符只能用于操作变量,不能直接用于操作数值或常量!例如 5++ 、 8-- 等写法都是错误的。
++ 和 -- 既可以出现在操作数的左边,也可以出现在右边,但结果是不同滴
左边
运行结果:
右边
运行结果:
赋值运算符
关于switch
1、switch 后面小括号中表达式的值必须是整型或字符型
2、case 后面的值可以是常量数值,如 1、2;也可以是一个常量表达式,如 2+2 ;但不能是变量或带有变量的表达式,如 a * 2。
3、可以把功能相同的 case 语句合并起来,如
数组
声明数组
数组的声明有两种方式
数组的元素类型 数组名[];
数组的元素类型[] 数组名;
分配空间
语法:
数组名 = new 数据类型[数组长度];
也可同时进行声明和分配空间:
数据类型[] 数组名 = new 数据类型[数组长度];
此时数组长度不能为空
初始化
数据类型 数组名[] = {值1,值2,值3...};
又或者
数据类型 数组名[] = new 数据类型[]{值1,值2,值3...};
此时数据类型后的[]中应为空
操作
以升序排序
Arrays.sort(数组名);
转为String
Arrays.toString(数组名);
PS:此方法并不是返回排序后的数组而是将原数组排序
Class类
重载
如果同一个类中包含了两个或两个以上方法名相同、方法参数的个数、顺序或类型不同的方法,则称为方法的重载,也可称该方法被重载了。如下所示 4 个方法名称都为 show ,但方法的参数有所不同,因此都属于方法的重载:
PS:方法的重载与方法的修饰符或返回值没有关系
成员变量和局部变量的区别
Java会给成员变量赋一个初始值
Java不会给局部变量赋初始值
成员变量和局部变量同名时,在方法内局部变量有更高的优先级
构造方法
构造方法是定义在Java类中的一个用来初始化对象的方法,名称与类名相同没有返回值。
当没有指定构造方法时,系统会自动添加无参的构造方法。
当有指定的构造方法时,无论是有参,无参的构造方法,都不会自动添加无参的构造方法。
static
使用 static 可以修饰变量、方法和代码块。被static修饰的变量叫类变量,否则叫实例变量;被static修饰的方法叫类方法或静态方法,否则叫实例方法。
类变量和类方法属于类,实例变量和实例方法属于对象。当类第一次使用时,只会给类方法和类变量分配内存,而不会给实例变量和实例方法分配内存。只有创建对象后,才会给实例变量和实例方法分配内存。类的所有对象共用类变量和类方法,而实例变量和实例方法只被所创建的对象使用。
静态成员可以使用类名直接访问,也可以使用对象名进行访问,推荐用类名访问。
实例方法可以调用该类中的实例方法或类方法,而类方法只能调用该类的类方法,不能直接调用实例方法。如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量。
在类的声明中,可以包含多个初始化块,当创建类的实例时,就会依次执行这些代码块。如果使用 static 修饰初始化块,就称为静态初始化块。
需要特别注意:静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量
例如:
运行结果:
通过输出结果,我们可以看到,程序运行时静态初始化块最先被执行,然后执行普通初始化块,最后才执行构造方法。由于静态初始化块只在类加载时执行一次,所以当再次创建对象 hello2 时并未执行静态初始化块。
封装
面向对象的三大特性:继承,封装和多态
封装的概念:将类的信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法实现对隐藏信息的操作和访问。
封装的好处:
- 只能通过规定的方法访问数据。
- 隐藏类的实例细节,方便修改和实现。
访问修饰符
|访问修饰符|本类|同包|子类|其他|
| ------------ | ------------ |
|private| √ ||||
|缺省/默认|√ |√ |||
|protected|√ |√ |√ ||
|public|√ |√ |√ |√ |
内部类
定义:
内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。作用
内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
内部类的方法可以直接访问外部类的所有数据,包括私有的数据
内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
3.分类
成员内部类
静态内部类
方法内部类
匿名内部类
成员内部类
内部类定义在外部类的内部,相当于外部类的一个成员变量的位置,内部类可以使用任意访问控制符,如 public 、 protected 、 private 等
内部类中定义的 test() 方法可以直接访问外部类中的数据,而不受访问控制符的影响,如直接访问外部类中的私有属性a
定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
外部类是不能直接使用内部类的成员和方法滴
可先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。
5.如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。如:
静态内部类
1、 静态内部类不能直接访问外部类的非静态成员,但可以通过** new 外部类().成员** 的方式访问
2、 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
3、 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
方法内部类
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
注意:由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和 static 修饰符。
继承
继承是Java面向对象编程的一个重要特点,指一种由已有类创建新类的机制。利用继承,可以先编写一个共有属性的一般类,再根据一般类编写具有特殊属性的新类。新类继承一般类的属性和方法,并根据需要增加新的属性和方法。
所谓子类继承父类的成员变量和方法,就好像他们是在子类中直接声明的一样,可以被子类中自己定义的任何实例方法操作和调用。
子类对父类成员变量和方法的继承性:
| |private|缺省/默认|protected|public|
| ----- | ----- |
|同一包||√|√|√|
|不同包|||√|√|
继承的初始化顺序
先初始化父类再初始化子类
先初始化属性,再初始化构造方法
总的顺序是先初始化父类的属性,再初始化父类的构造方法,然后初始化子类的属性,最后初始化子类的构造方法。
重写@override
如果子类对继承父类的方法不满意,可以重写从父类继承的方法,当调用方法时会优先调用子类的方法。
注意:
- 返回值类型
- 方法名
- 参数类型及个数
都要与父类继承的方法相同,才叫方法的重写
final
final关键字可以修饰类,方法,属性和变量
final修饰类,则类不允许被继承
final修饰方法,则方法不允许被覆盖(重写)
final修饰属性,则该类的属性不会进行隐式的初始化(java自动赋初值),初始化时必须赋一个值,或在构造方法中赋值(二选一)
final修饰变量,则该变量只能赋一次值,即为常量
super
在子类的内部使用,可以代表父类对象。既可以访问父类的属性也可以访问父类的方法。
注意:
子类的构造过程中必须调用父类的构造方法。
如果子类的构造方法中没有显式调用父类的构造方法,则系统默认调用父类无参的构造方法,即隐式调用,super();
如果显式调用构造方法,必须在子类构造方法的第一行。
如果子类构造中既没有显式调用父类的构造方法,而父类又没有无参的构造方法,则编译出错。
Object类
Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另一个类,那么这个类默认继承Object类。
Object类中的方法,适合所有子类。
多态
java中的多态
字面上的意思是对象的多种形态。
- 引用多态:父类的引用既可以指向本类的对象,也可以指向子类的对象,如:
Animal obj1 = new Animal();//指向本类
Animal obj2 = new Dog();//指向子类
- 方法多态:
创建本类对象时,调用的方法为本类方法
创建子类对象时,调用的方法是子类重写的方法或继承的方法,不能调用子类独有的方法。
/**
* 接上段代码
*/
obj1.eat();//调用本类方法
obj2.eat();//调用子类重写或继承的方法
//obj2.watchDoor();不能调用子类独有的方法
多态中的引用类型转换
- 向上类型转换(隐式/自动类型转换),是小类型到大类型的转换
Dog dog = new Dog();
Animal animal = dog;//向上类型转换
- 向下类型转换(强制类型转换),是大类型到小类型的转换,存在溢出的风险
Dog dog = new Dog();
Animal animal = dog;
Dog dog = (Dog)animal;
/*
* Animal animal = new Animal();
* Dog dog = (Dog)animal;
* 编译不出错,运行过程中出错。
*/
instanceof
instanceof运算符是二目运算符,左边的操作元是一个对象,右边是一个类。当左边的对象是右边的类或子类创建的对象时,返回true,否则返回false。
/**
* 此例可以转换
*/
Dog dog = new Dog();
Animal animal = dog;
if(animal instanceof Dog){
Dog dog = (Dog) animals;
}else{
System.out.println("无法转换");
}
抽象类abstract
定义
使用abstract修饰的类叫抽象类
应用场景
在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道子类如何实现这些方法
从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性
作用
限制规定子类必须实现某些方法,但不关注实现细节。
使用规则
abstract定义抽象类
abstract定义抽象方法,只有声明,不需要实现
包含抽象方法的类是抽象类
抽象类中可以包含普通方法,也可以没有抽象方法
抽象类不能直接创建,可以定义引用变量
/**
* 父类
*/
public abstract class Animal {//abstract定义抽象类
public abstract void eat();
public abstract void work();//abstract定义抽象方法,只有声明,不需要实现
}
/**
* 子类1
*/
public class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
@Override
public void work() {
System.out.println("看门");
}
}
/**
* 子类2
*/
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");//子类要实现父类的抽象方法
}
@Override
public void work() {
System.out.println("捉老鼠");
}
}
/**
* 初始化
*/
public class test {
public static void main(String[] args) {
Animal dog = new Dog();//抽象类不能直接创建,可以定义引用变量,
//不能直接创建,Animal animal = new Animal();
dog.eat();
dog.work();
Animal cat = new Cat();
cat.eat();
cat.work();
}
}
接口
概念
类是一种具体实现体,而接口定义了某一些类所需要遵守的规范,接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某些方法。
定义
接口通过interface关键字来定义:
修饰符 interface 接口的名字
接口就是用来被继承,被实现的,修饰符不能使用private和protected,一般建议用public。
接口中的属性只能是常量,即定义是不添加** public static final **修饰符,系统也会加上。
接口中的方法只能是抽象方法,即使定义时不添加pubic abstract 修饰符,系统也会自动加上。
即使不给接口添加abstract关键字,系统也会默认加上。
使用
接口可以同时实现多个,必须放在继承之后。
同abstract类似,接口不能直接创建,但可以用接口的引用指向一个实现了接口的对象:
//ClassA implements InterfaceA
InterfaceA a = new ClassA();
abstract类与接口的比较
abstract类和接口都可以有abstract方法
接口只可以有常量,不能有变量,而abstract类中既可以有常量也可以有变量
abstract类可以有非abstract方法,接口不可以
在设计程序时应当根据具体的分析来确定是使用抽象类还是接口。abstract类除了提供重要的需要子类去实现的abstract方法外,还可以提供子类可以继承的变量和非abstract方法。如果某个问题需要使用继承才能更好的解决,比如子类除了需要实现父类的abstract方法外,还需要从父类继承一些变量或一些重要的非abstract方法,就可以考虑使用abstract类。如果某个问题不需要继承,只是需要若干个类给出某些重要的abstract方法的实现细节,就可以考虑使用接口。
匿名内部类
- 实例1:不使用匿名内部类来实现抽象方法
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();//从这对比下面的例子可以看出匿名类实际上是子类
p.eat();
}
}
运行结果:eat something
可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类
- 实例2:匿名内部类的基本实现
abstract classPerson {
public abstract void eat();//匿名类的类体不可以声明static成员变量和static方法
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
可以看到,我们直接将抽象类Person中的方法在大括号中实现了
这样便可以省略一个类的书写
并且,匿名内部类还能用于接口上
- 实例3:在接口上使用匿名内部类
interface Person {
public void eat();
}
public class Demo {
public static void main(String[] args) {
Person p =new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
运行结果:eat something
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
摘自匿名内部类的解释
- 高阶:
interface Product{
public double getPrice();
public String getName();
}
public class TestAnonymous{
public void test(Product p){
System.out.println("购买了一个"+p.getName()+",花掉了"+p.getPrice());
}
public static void main(String[] args){
TestAnonymous ta = new TestAnonymous();
ta.test(new Product(){
public double getPrice(){
return 567;
}
public String getName(){
return "AGP显卡";
}
});
}
}
异常类
所谓异常就是程序运行时可能出现一些错误。
所有的异常类都是Exception的子类,而Exception和Error又同为Throwable的子类。
Error类的子类又有虚拟机错误(VitrualMachineError)和线程锁死(ThreadDeath)等一类,这类错误一般不会出现,出现的话程序就彻底崩溃了。
Exception类一般是由编码,环境,用户操作输入出现等问题所引起的,其子类包括运行时异常(RunTimeException)和检查异常。
运行时异常又包含空指针异常,数组下标越界异常,类型转换异常,算术异常等子类,它们都是由JVM自动捕获自动抛出,出现运行时异常一般是代码编写问题。
检查异常则多种多样,如文件异常(IOException),SQL异常(SQLException)等,这类异常需要自己手动添加捕获处理语句。
try-catch语句
try-catch语句可以用来处理异常,其中try部分捕获异常,catch部分处理异常。
try{
//包含可能发生异常的语句
}
catch(ExceptionSubClass e){
//处理
}
catch(ExceptionSubClass2 e){
...
}
catch中的参数都是Exception类或Exception的子类,捕获异常时应遵循先小后大的原则。
异常对象可以通过以下方法得到或输出错误信息:
public String getMessage();
public void printStackTrace();
public String toString();
finally子语句
语法格式:
try{}
catch(ExceptionSubClass e){}
finally{}
其执行机制是,在执行try-catch语句后,执行finally子语句。也就是说,无论在try部分是否发生过异常,finally子语句都会被执行。
注意:
如果在try-catch语句中执行了return语句,那么finally子语句仍然会被执行
如果在try-catch语句中执行了程序退出代码,即执行了System.exit(0);,将不执行finally子语句。
如果try-catch-finally语句都没有return,则会调用语句块之外的return,注意,如果方法体中执行了return语句,则其后的语句都不会再执行。
异常抛出
public void 方法名(参数) throws 异常{
//调用会抛出异常的方法或:
throw new Exception();
}
throw
将产生的异常抛出,用在方法体内
throws
声明将要抛出何种类型的异常,可以抛出一个或多个异常
自定义异常
需要继承Exception类或其子类。
异常链
一个异常如果是由另一个异常引起的,这种链式反应就可以叫做异常链。如在try-catch捕获到一个异常后再创建一个新的异常,从而将旧的异常包装成新的异常,然后抛出新的异常。
void test(){
try{
...
}catch(Exception1 e){
Exception2 e2 = new Exception2("异常");
e2.initCause(e2);
throw e2;
//或者
// Exception2 e2 = new Exception2(e);
}
}
String
首先需要注意的是,和Java中的包装类一样,当通过赋值等号给变量赋相同的值的时候,系统会默认将其指向同一块内存,而通过new方法创建对象时,不管内容是否相同,都会分配一块新的内存,此时使用 ”==” 比较时也为 ”false”。如果只需比较内容是否相同,应使用 ”equals()” 方法。
** ==: 判断两个字符串在内存中首地址是否相同,即判断是否是同一个字符串对象 **
** equals(): 比较存储在两个字符串对象中的内容是否一致 **
如:
结果:
字符串的不变性
String 对象创建后则不能被修改,是不可变的,所谓的修改其实是创建了新的对象,所指向的内存空间不同。如果需要一个可以改变的字符串,我们可以使用StringBuffer或者StringBuilder。
String类常用方法
注意:
字符串 str 中字符的索引从0开始,范围为 0 到 str.length()-1
使用 indexOf 进行字符或字符串查找时,如果匹配返回位置索引;如果没有匹配结果,返回 -1
使用 substring(beginIndex , endIndex) 进行字符串截取时,包括 beginIndex 位置的字符,不包括 endIndex 位置的字符
StringBuilder和StringBuffer
因为String字符串具有不变性,每次操作字符串都要创建新的字符串对象,当频繁操作字符串时,就会额外产生很多临时变量。使用 StringBuilder 或 StringBuffer 就可以避免这个问题。至于 StringBuilder 和StringBuffer ,它们基本相似,不同之处,StringBuffer 是线程安全的,而 StringBuilder 则没有实现线程安全功能,所以性能略高。因此一般情况下,如果需要创建一个内容可变的字符串对象,应优先考虑使用 StringBuilder 类。
常用方法:
相比String类,StringBuilder和StringBuffer多了追加和插入操作。这些操作修改了 str 对象的值,而没有创建新的对象,这就是 StringBuilder 和 String 最大的区别。
Java中的包装类
Java中有int、float、double、boolean、char 等基本数据类型,基本类型不具备对象的特性的,不能调用方法、功能简单。。。,为了让基本数据类型也具备对象的特性, Java 为每个基本数据类型都提供了一个包装类,这样我们就可以像操作对象那样来操作基本数据类型。
基本类型和包装类之间的对应关系:
包装类主要提供了两大类方法:
将本类型和其他基本类型进行转换的方法
将字符串和本类型及包装类互相转换的方法
以Interger举例
Integer 包装类的构造方法:
如下代码所示:
Integer包装类的常用方法:
基本类型和包装类之间的互相转换(以 Integer 为例)
在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了。
装箱:把基本类型转换成包装类,使其具有对象的性质,又可分为手动装箱和自动装箱
拆箱:和装箱相反,把包装类对象转换成基本类型的值,又可分为手动拆箱和自动拆箱
Java 中基本类型和字符串之间的转换(以 Integer 为例)
基本类型转换为字符串有三种方法:
使用包装类的 toString() 方法
使用String类的 valueOf() 方法
用一个空字符串加上基本类型,得到的就是基本类型数据对应的字符串
字符串转换成基本类型有两种方法:
调用包装类的 parseXxx 静态方法
调用包装类的 valueOf() 方法转换为基本类型的包装类,会自动拆箱
PS:其他基本类型与字符串的相互转化这里不再一一列出,方法都类似
常用类
使用 Date 和 SimpleDateFormat 类表示时间
在程序开发中,经常需要处理日期和时间的相关数据,此时我们可以使用 java.util 包中的 Date 类。这个类最主要的作用就是获取当前时间,我们来看下 Date 类的使用:
使用 Date 类的默认无参构造方法创建出的对象就代表当前时间,我们可以直接输出 Date 对象显示当前的时间,显示的结果如下:
其中, Wed 代表 Wednesday (星期三), Jun 代表 June (六月), 11 代表 11 号, CST 代表 China Standard Time (中国标准时间,也就是北京时间,东八区)。
从上面的输出结果中,我们发现,默认的时间格式不是很友好,与我们日常看到的日期格式不太一样,如果想要按指定的格式进行显示,如 2014-06-11 09:22:30 ,那该怎么做呢?
此时就到了 java.text 包中的 SimpleDateFormat 类大显身手的时候了!!可以使用 SimpleDateFormat 来对日期时间进行格式化,如可以将日期转换为指定格式的文本,也可将文本转换为日期。
- 使用 format() 方法将日期转换为指定格式的文本
代码中的 “yyyy-MM-dd HH : mm : ss” 为预定义字符串, yyyy 表示四位年, MM 表示两位月份, dd 表示两位日期, HH 表示小时(使用24小时制), mm 表示分钟, ss 表示秒,这样就指定了转换的目标格式,最后调用 **format() **方法将时间转换为指定的格式的字符串。
运行结果:** 2014-06-11 09:55:48 **
2 . 使用 parse() 方法将文本转换为日期
代码中的 “yyyy年MM月dd日 HH : mm : ss” 指定了字符串的日期格式,调用 **parse() **方法将文本转换为日期。
运行结果:
一定要注意哦:
1、 调用 SimpleDateFormat 对象的 parse() 方法时可能会出现转换异常,即 ParseException ,因此需要进行异常处理
2、 使用 Date 类时需要导入 java.util 包,使用 SimpleDateFormat 时需要导入 java.text 包
Calendar 类的应用
Date 类最主要的作用就是获得当前时间,同时这个类里面也具有设置时间以及一些其他的功能,但是由于本身设计的问题,这些方法却遭到众多批评,不建议使用,更推荐使用 Calendar 类进行时间和日期的处理。
java.util.Calendar 类是一个抽象类,可以通过调用 getInstance()静态方法获取一个 Calendar 对象,此对象已由当前日期时间初始化,即默认代表当前时间,如 Calendar c = Calendar.getInstance();
那么如何使用 Calendar 获取年、月、日、时间等信息呢?我们来看下面的代码:
其中,调用 Calendar 类的 getInstance() 方法获取一个实例,然后通过调用 get() 方法获取日期时间信息,参数为需要获得的字段的值, Calendar.Year 等为 Calendar 类中定义的静态常量。
运行结果:
Calendar 类提供了 getTime() 方法,用来获取 Date 对象,完成 Calendar 和 Date 的转换,还可通过 getTimeInMillis() 方法,获取此 Calendar 的时间值,以毫秒为单位。如下所示:
运行结果:
使用 Math 类操作数据
Math 类位于 java.lang 包中,包含用于执行基本数学运算的方法, Math 类的所有方法都是静态方法,所以使用该类中的方法时,可以直接使用类名.方法名,如: Math.round();
常用的方法:
Java集合框架
Java中的集合类是一种工具,就像是容器,储存任意数量的具有共同属性的对象
作用
在类的内部,对数据进行组织
简单而快速的搜索大数量的条目
有的集合接口,提供了一系列排列有序的元素,并且可以在序列中间快速的插入或者删除有关元素
有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型
与数组的对比--为何选择集合而不是数组
数组的长度固定,集合长度可变
数组只能通过下标访问元素,类型固定,而有的集合可以通过任意类型查找所映射的具体对象
集合框架
Collection存储单个对象,而Map存储键值对。
泛型
集合中的元素可以是任意的对象(其保存的是对象的引用),如果把某个对象放入集合,则会忽略他的类型,而把他当作Object处理,所以通过get方法取出后还需强制类型转化。
泛型则是规定了某个集合只可以存放特定类型及其子类型的对象,会在编译期间进行类型检查,可以直接按指定类型获取集合元素,不需要类型转换。
泛型的声明方法只比普通的集合多一个尖括号加指定类型,如:
public List courses= new ArrayList();
操作与普通集合完全相同。
** 注意:**
泛型集合中的限定类型,不能使用基本数据类型
可以通过使用包装类限定允许存入的基本数据类型
PS:不适用泛型的普通集合能添加基本类型是因为自动装箱将基本类型转成了包装类。
Collection
Collection接口是List,Set和Queue接口的父类,定义了增删改查的操作。
List
List是元素有序并且可以重复的集合,被称为序列
List可以精确的控制每个元素的插入位置,或删除某个位置的元素
ArrayList--数组序列,是List的一个重要实现类,因为List是接口不能直接创建对象,需要通过ArrayList创建,如:
public List coursesToSelect= new ArrayList();
List中,多次添加某个对象,则会创建多个对象,但其指向同一块内存
ArrayList底层是由数组实现的
Add
Course cr1 = new Course("1", "数据结构");
coursesToSelect.add(cr1);//直接添加对象
Course cr2 = new Course("2", "C语言");
coursesToSelect.add(0, cr2);//将对象添加到指定位置,原位置已有内容则和后面内容一起顺延一位
// 当添加的位置没有按顺序递增时会抛出数组下标越界异常
// Course cr3 = new Course("3", "test");
// coursesToSelect.add(4, cr3);
Course[] course = { new Course("3", "离散数学"), new Course("4", "汇编语言") };
coursesToSelect.addAll(Arrays.asList(course));//通过Array.asList()添加一个对象数组
Course[] course2 = { new Course("5", "高等数学"), new Course("6", "大学英语") };
coursesToSelect.addAll(2, Arrays.asList(course2));//将对象数组添加到指定位置
Remove
coursesToSelect.remove(cr1);//删除指定某位置对象
Course cr1 = (Course) coursesToSelect.get(0);
coursesToSelect.remove(cr1);//删除指定对象,此时cr1获得的是引用,所以如果list中有两个执向相同地址的引用,不管get方法取自谁,都会先删除排序靠前的那个元素,当执行两次删除方法时,则删除掉两个元素
//注意
//Course cr1 = new Course("参数");
//coursesToSelect.remove(cr1);
//这样并不能删除指定对象,即便参数相同,但因为引用不同,指向内存不同,故无法删除
Course[] courses = { (Course) coursesToSelect.get(4), (Course) coursesToSelect.get(5) };
coursesToSelect.removeAll(Arrays.asList(courses));//批量删除,即使取出序号不相邻也可执行,其内部机制是将对象数组取出,分别执行删除单个对象
Modify
coursesToSelect.set(4, new Course("7", "毛概"));//修改指定位置元素
Get
/**
* 通过for循环取得List中的元素的方法
*
* @param args
*/
public void testGet() {
int size = coursesToSelect.size();
System.out.println("有如下课程待选:");
for (int i = 0; i < size; i++) {
Course cr = (Course) coursesToSelect.get(i);
System.out.println("课程:" + cr.id + ":" + cr.name);
}
}
/**
* 通过迭代器Iterator来遍历List
*
* @param args
*/
public void testIterator() {
// 通过集合的iterator方法,取得迭代器的实例
Iterator it = coursesToSelect.iterator();
System.out.println("有如下课程待选(通过迭代器访问):");
while (it.hasNext()) {
Course cr = it.next();
System.out.println("课程:" + cr.id + ":" + cr.name);
}
}
/**
* 通过for each方法访问集合元素
*
* @param args
*/
public void testForEach() {
System.out.println("有如下课程待选(通过for each访问):");
for (Object obj : coursesToSelect) {
Course cr = (Course) obj;
System.out.println("课程:" + cr.id + ":" + cr.name);
}
}
Set
Set与Collection最大的不同是其内部元素是无序且不可重复的,被称为集(这里的无序是指引用不同)
HashSet--哈希集,是Set的一个重要实现类,同ArrayList的地位一样,在创建对象时使用:
Set courses = new HashSet();
因为Set是无序的,所以没有get方法,也不能通过for循环get出遍历全部对象,自能通过foreach和iterator遍历出全部对象,并且每次遍历出的顺序都不一样。
Set中,添加某个对象,无论添加多少次, 最终只会保留一个该对象(的引用), 并且,保留的是第一次添加的那一个
Set中还可以添加一个null元素,但也只能添加一个
Map
Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value
Map中的键值对以Entry类型的对象实例形式存在
键(key值)不可重复,value值可以
每个键最多只能映射到一个值
Map接口提供了分别返回key值集合,value值集合以及Entry(键值对)集合的方法
Map支持泛型,形式如:Map < k,V >
HashMap类
HashMap是Map的一个重要实现类,也是最常用,基于哈希表实现
HashMap中的Entry对象是无序排列的
key值和value值都可以为null,但是一个HashMap只能有一个key值为null的映射(key值不可重复)
增
通过put方法就可以添加新的元素,已存在key则修改Value值,不存在则创建
students.put(ID, newStudent);
删
通过remove方法就可以指定key值的元素,当key值不存在时返回null
students.remove(ID);
改
put和replace都支持修改元素操作,replace是JDK8中新增加的方法,实现机制更简单。二者都会首先判断key值是否存在,不存在会返回null,存在则修改。
students.put(ID,newValue);
students.replace(k, v);
查
利用keySet遍历Map:keySet方法返回是key值的Set集,遍历此Set集然后通过key就可get到对应的value。通过get方法取出Value时,当存在key就返回对应Value,不存在则返回null。
Set keySet = students.keySet();
// 遍历keySet,取得每一个键,再调用get方法取得每个键对应的value
for (String stuId : keySet) {
Student st = students.get(stuId);
if (st != null)
System.out.println("学生:" + st.name);
System.out.println("取得键:" + stuId);
}
利用entrySet遍历Map:entrySet方法返回的是Entry键值对的对象,通过getkey和getvalue取出键值对的键和值
Set> entrySet = students.entrySet();
for (Entry entry : entrySet) {
System.out.println("取得键:" + entry.getKey());
System.out.println("对应的值为:" + entry.getValue().name);
}
关于集合框架的其他知识点
Contains
List中的contains
一些方法如contains,内部机制是首先遍历集合再对比object,相同返回true,不同返回false,这些方法都要用到equals,但equals默认对比的是两个对象,即使两个对象内容相同,如果内存不同还是会返回false,此时就要重写equals方法
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;//首先比较对象引用,引用相同返回true
if (obj == null)
return false;//当对象是null时,则返回false
if (!(obj instanceof Course))
return false; // 当obj不是由Course或其子类创建的对象时返回false
Course other = (Course) obj;//此时开始对比对象内容,先将Object类转为子类
if (name == null) {
if (other.name != null)
return false; //当当前name为null,而对比对象name不为null,返回false
} else if (!name.equals(other.name))
return false;//当当前对象不为null,对比对象name不等于当前对象name,返回false
return true;//其他则返回true
}
此例可以作为重写equals的模板
Set中的contains
Map中的contains和List的不同是其要对比HashCode和equals两个方法,所以只比较内容应重写这两个方法。eclipse提供了自动重写HashCode和equals,在源码(Alt + Shift + S)中可以找到
hashCode的重写
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
Map中的contains
Map中有containsKey和ContainsValue两个方法判断是否包含对应的key和value,也是遍历和equals,同Set一样需要重写HashCode和equals方法。
indexOf和lastIndexOf
indexOf和lastIndexOf的实现机制是首先遍历所有整个集合,当集合中的对象等于指定对象时返回序号,不包含则返回-1,当出现同样的元素时,index返回正序第一个,lastindex返回倒序第一个。
因为也要调用equals方法,所以当需要对比内容时也要重写equals。
Collections.sort排序
此方法可以对序列进行排序,数字按升序,字符串按先数字,再大写字母,再小写字母的顺序。
sort()中传入的参数需要实现** Comparable **接口或一个list,一个Comparator(比较规则)
另外Collections还有其他的方法:
Collections.shuffle();//随机打乱顺序
Collections.reverse();//倒序排列
Collections类操作集合时是对集合进行操作,并不会产生新的集合,不需要定义集合接收。
Comparable接口--可比较的
实现该接口表示:这个实例可以比较大小,可以进行自然排序
定义了默认的比较规则
其实现类需实现comparaTo()方法
comparaTo()方法返回正数表示大,负数表示小,0表示相等
具体使用方法:
首先要领List中的对象实现Comparable接口,Comparable接口中有compareTo方法定义比较规则
public class Student implements Comparable {
//Comparable接口也支持泛型
...
//@Override
public int compareTo(Student o) {
return this.id.compareTo(o.id);//规则是比较Student的id
}
}
Comparator接口--比较工具接口
用于定义临时比较规则,而不是默认比较规则
其实现类需要实现conpara()方法
Comparator和Comparable都是Java集合框架的成员
使用Comparator时不需要实现Comparable接口,而是要定义一个新的类实现Comparator接口定义比较规则
public class StudentComparator implements Comparator {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);//规则是比较Student的name
}
}
使用时要在sort中传入list和比较规则两个参数
Collections.sort(studentList, new StudentComparator());
END
好像不能自动为MarkDown文档生成目录,所以打个广告,想要阅读体验更好可以移步我的博客:
小牛的博客