JAVA学习第二周:面向对象进阶

第一天

  • 方法重载

  • 初始化块

  • 封装类型

  • String类详解

多态的实现策略之一--------方法重载(Overload)

方法的重载(overload)是多态的一种实现策略,方法的重载表现为在一个类中包含多个同名的方法,但是每个方法必须保证方法中的参数列表(类型,个数,顺序)任意一项不一致,方法重载与返回值无关。

public class PrintDemo {
    
    public void write(int i){
        System.out.println(i);
    }
    
    public void write(float f){
        System.out.println(f);
    }
    
    public void write(String s){
        System.out.println(s);
    }
    
    public static void main(String[] args){
        PrintDemo pd = new PrintDemo();
        pd.write(100);
        pd.write(3.14F);
        pd.write("helloworld");
    }    
}

方法重写(Override)和重载(Overload)区别:

  1. 方法重写发生在存在继承关系的子类中,由子类重写父类的方法;方法的重载一般发生在同一个类中

  2. 方法重写必须保证方法的返回值类型与父类一致;方法的重载与返回值无关

  3. 方法重写时必须保证方法中的参数列表与父类一致;方法重载必须保证每个方法的参数列表不一致

  4. 共同点:不论方法重写还是重载,都必须保证方法名一致

游离块(初始化块)

在一个java类中如果存在多个构造器,每个构造器中都有重复代码时,可以将重复的初始代码归纳到一个游离块中,统一执行,从而解决构造器中代码重复的问题,游离块一般在构造器执行之前先执行,在构造对象时隐式调用,本质上就是一个方法的执行体

public class Config {
    
    public Config(){
        System.out.println("默认构造器的操作");
    }
    
    public Config(String key,String value){
        System.out.println("设置单个键值对");
    }
    
    public Config(Properties props){
        System.out.println("设置一组信息");
    }
    
    //初始化块(游离块)
    {
        System.out.println("初始化。。。");
    }
    
    public static void main(String[] args) {
        new Config("version","1.8");
    }
}
​

基本数据类型封装类

在java中每一个基本数据类型都有一个与之对应的引用类型,基本类型数据一般用于算数运算,布尔逻辑,关系等运算,但是无法进行对象操作(方法调用,属性调用);但是,通过封装类型可以实现基本类型的对象操作,比如进行字符串与基本类型数据之间的转换,进制转换。

byte(Byte)

short(Short)

int(Integer)

long(Long)

float(Float)

double(Double)

char(Character)

boolean(Boolean)

  • 自动装箱(将一个基本类型的数据自动打包成封装类型)

  • 自动拆箱(将一个引用类型的对象解析为一个基本类型数据)

//自动装箱
Integer i = 10; 
​
//自动拆箱
int j = i;

对基本类型数据的封装类型使用常见于字符串与该基本类型数据之间的转换

//将字符串转换为int类型
int i = Integer.parseInt("123");
int i2 = new Integer("123");
​
//将基本类型的数据转换为字符串类型
Integer j = i;
String s = j.toString();
String s2 = 123 + "";

String类

String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。

String类常用构造器

  • String()

  • String(String content)

  • String(char[] c)

  • String(char[] c,int offset,int len)

  • String(byte[] b)

  • String(byte[] b,offset,int len)

String类常用方法

  • char charAt(int index) :获取指定索引处的字符值

  • int compareTo(String str):按字典比较两个字符串的顺序,返回两个字符串间隔的字符数

  • String concat(String str):字符串连接,返回新的字符串

  • boolean contains(String str):判断当前字符串是否包含指定的子串

  • boolean endsWith(String suffix):判断字符串是否以指定的后缀结束

  • boolean equals(String str):判断两个字符串字面值是否一致

  • boolean equalsIgnoreCase(String str):忽略大小写比较两个字符串是否相等

  • byte[] getBytes():将字符串转换为字节数组

  • int indexOf(String str):获取指定字符串在当前字符串中第一次出现的位置

  • boolean isEmpty():判断字符串对象是否为空(非null),当且仅当length()为0时输出true

  • int length():获取字符串中字符的个数

  • boolean matches(String regex):判断字符串是否匹配指定的正则表达式

  • String replace(CharSequence oldValue,CharSequence newValue):字符串替换,后者替换前者

  • String[] split(String regex)根据指定的正则表达式将字符串截取并生成新的字符串数组

  • String substring(int beginIndex,int endIndex):字符串截取

  • String toUpperCase(String str)/String toLowerCase(String str):大小写转换

  • String trim():去除字符串前置与后置空格

  • static String join(String delimiter,CharSequence... c):使用指定分隔符对将多个字符序列拼接并返回

第二天

  • String与StringBuffer,StringBuilder

  • static关键字

  • 设计模式之单例模式

  • JDK1.7新增Objects类

  • final关键字

  • 抽象类

  • 设计模式之模板方法

String与StringBuffer,StringBuilder

String在java中表示定长字符串,一旦对String初始化,则无法修改内容,除非重新赋值(创建新对象),所以String在进行大量字符串拼接时会频繁创建新对象,从而影响程序的运行效率;

StringBuffer是一个长度可变的字符序列,可以动态在原来字符串缓冲区的基础上追加(append)或者插入(insert)新的字符序列,而不会修改对象的内存地址,所以使用StringBuffer在完成字符串拼接时效率较高,另外StringBuffer是线程安全的实现,所以在线程并发操作时效率会有所影响

StringBuilder跟StringBuilder具备相同的API(构造器,方法等),但是StringBuilder是不执行同步(线程不安全),因此效率会高于StringBuffer.

static关键字

static可以用于修饰内部类,属性,方法,游离块;被static修饰的元素与实例无关(与对象无关),与类直接相关,一旦类被加载则static修饰的元素都会被初始化,static修饰的元素有最高存活优先级,即便普通引用对象被GC回收时,static修饰相关资源不会立即回收;被static修饰的属性,方法,内部类称之为静态属性,静态方法,静态内部类,这些元素都无需创建对象调用,可以直接通过类名(加".")进行调用,一般static用于常量定义,一些工具类中的方法,需要反复执行,但是不需要反复创建。

  • 实例变量,实例方法(需要通过对象进行调用,属于对象的)

  • 类变量,类方法(直接与类相关的静态元素)

关于static元素的注意事项

  1. 不允许在静态方法中使用非静态元素(静态方法被调用时可能对象还未创建,非静态元素都需要通过对象调用)

  2. 静态元素只会在类加载时初始化一次

static语句块

通过在游离快前加上static关键,以表示当前语句块是一个静态游离快,静态游离快一旦类被加载之后,static语句块会立即执行,并且只会执行一次,不会因为对象的多次创建而执行多次。

public class Block{
    static{
        //...执行一些程序运行时不需要反复执行的代码,比如连接数据库时的驱动加载,常用资源的获取
    }
}

设计模式之单例模式

设计模式,一些用于解决某些特定需求的通用设计方法,java中常用设计模式包含23种;

单例模式(Singleton),在程序的运行期间某个对象的实例始终只会存在一个,不存多少次获取,获得的始终是同一个实例对象,单例模式的设计原则:

  • 构造器必须私有化,防止外界随意访问

  • 提供static方法用于获取对象实例

单例模式的实现方式包含三种:

  1. 饱汉模式

  2. 饿汉模式

  3. 双重锁模式

饱汉模式(延迟创建)

public class Singleton{
    private static Singleton instance;
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

饿汉模式(饥渴创建)

public class Singleton{
    private static Singleton instance = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return instance;
    }
}

final关键字

final可以用于修饰类,方法,属性,被final修饰的元素:

  1. final修饰的类不可被继承(断子绝孙类)

  2. final修饰的变量不可被改变(常用于与static组合进行常量的声明)

  3. final修饰的方法是不允许被重写

final、finally,finalize区别?

Objects类

jdk1.7之后新增一个对Object对象进行空指针安全操作的相关工具类,里面提供了一些列空指针安全的方法

String s1 = null;
String s2 = new String("hello");
//      if(s1 != null){         
//          System.out.println(s1.equals(s2));
//      }
System.out.println(Objects.equals(s1, s2));
System.out.println(Objects.deepEquals(s1, s2));
​
//基于空指针安全的hashcode获取    
System.out.println(Objects.hashCode(s1));
//基于空指针安全的对象是否为空判断
System.out.println(Objects.isNull(s1));
​
//基于空指针安全的对象是否不为空判断
System.out.println(Objects.nonNull(s2));
​
//基于空指针安全的对象toString操作
System.out.println(Objects.toString(s1));

抽象类(abstract)

抽象类是java中的重要特点之一,当一个类中包含的一些信息不足以描述一个对象时该类可以定义为抽象类,可以将一个类,方法使用abstract关键字修饰,使用abstract修饰的类无法被实例化,使用abstract修饰的方法不能有具体的实现,必须由子类实现

abstract class 类名称{

​ //已实现方法

//未实现方法(abstract method)

}

public abstract class Animal {
    
    private String name;
    
    //抽象方法
    public abstract void sleep();
    
    public void setName(String name){
        this.name = name;
    }
​
}
​

关于抽象类

  1. 抽象类中不一定包含抽象方法,包含抽象方法的类一定是抽象类

  2. 抽象方法不能有实现体,只包含方法定义

  3. 子类对抽象类继承了则子类必须实现抽象类中的抽象方法

  4. 抽象类也可继承抽象类(只能继承一个直接抽象类)

  5. 抽象类包含构造器,但是无法实例化

设计模式之模板方法模式

在类中定义了某个算法的骨架,但不具体实现,而在其子类中实现,例如,银行计算利息,都是利率乘以本金和存款时间,但各种存款方式计算利率的方式不同,所以,在账户这个类的相关方法里,只搭出算法的骨架,但不具体实现。具体实现由各个子类来完成;以上定义算法骨架由子类实现的设计模式称之为模板方法模式。

/**
 * 账户类
 * @author mrchai
 */
public abstract class Account {
​
    private double interest; //利息
    private double fund; //本金
    
    public Account(double fund) {
        this.fund = fund;
    }
    
    /**
     * 获取利息
     * @return
     */
    public double getInterest(){
        return fund * getInterestRate();
    }
    
    /**
     * 由于不同存款类型和时间决定不同的利率,所以利率的获取无法直接确定
     * @return
     */
    public abstract double getInterestRate();
}
​

子类A:

/**
 * 定期账户
 * @author mrchai
 */
public class AccountTypeA extends Account{
​
    private int year;   //存款年限
    private double base = 0.01; //基本利率
    
    public AccountTypeA(double fund,int year) {
        super(fund);
        this.year = year;
    }
​
    @Override
    public double getInterestRate() {
        return year * base;
    }
​
}

子类B:

/**
 * 活期账户
 * @author mrchai
 *
 */
public class AccountTypeB extends Account {
​
    private double base = 0.005;//基本利率
    
    public AccountTypeB(double fund) {
        super(fund);
    }
​
    @Override
    public double getInterestRate() {
        return base;
    }
​
}
​

测试类:

public class AccountTest {
​
    public static void main(String[] args) {
        //定义本金
        double fund = 1000000;
        //创建A型账户
        AccountTypeA a = new AccountTypeA(fund,5);
        System.out.println(a.getInterest());
        
        //创建B型账户
        AccountTypeB b = new AccountTypeB(fund);
        System.out.println(b.getInterest());
    }
​
}

面向对象高级

  • 多态

  • 接口

  • 内部类

多态

在java中,方法和属性都是可以以多态的形式存在的,方法的多态主要体现方法的重写与重载;属性的多态一般表现形式为父类引用指向子类对象(向上转型);一个引用类型变量既可以指向该引用类型自身,也可以指向该类型子对象,该操作称之为多态,或者动态绑定。

public abstract class Animal{
    public abstract void sleep();
} 
​
public class Dog extends Animal{
    public void sleep(){
        System.out.println("呼呼大睡。。。。");
    }
}
​
public class Test{
    public static void main(String[] args){
        //父类引用指向子类对象
        Animal a = new Dog();
    }
}

多态的优势:

  1. 降低了类之间的耦合

  2. 面向抽象编程

  3. 提高程序的可扩展性

多态中关于对象转型问题:

可以使用父类引用指向子类对象(上转型);反过来子类引用可以指向父类对象(下转型),但是被下转型的父类对象原本就应该是子类的对象,否则会出现:java.lang.ClassCastException异常(造型失败)

//上转型
Animal a = new Dog("旺财");
​
Dog dog = (Dog)new Animal(); //java.lang.ClassCastException
​
//下转型
Dog d = (Dog)a;

接口(interface)

interface,是一种比抽象类更纯粹的结构,接口中只能存在抽象方法和常量(接口是一组抽象方法和常量的集合),接口并不是类,所以没有构造器;一般可以由具体类对接口进行实现,并且具体类必须实现接口中的所有方法

接口语法:

<modifier> interface <name>[extends <superinterface>] {

​ <attribute_declarations>

<method_declarations>

}

接口(Flyable.java):

interface Flyable{
    //常量定义(系统自动补充:public static final String TYPE = "bird")
    String TYPE = "bird";
    //抽象方法定义(系统自动补充:public abstract void fly())    
    void fly();
}

实现类(Bird.java):

class Bird implements Flyable{
    
    public void fly(){
        System.out.println("自由自在翱翔天际!");
    }
}

以上为一个标准接口的定义以及实现类对接口的具体实现,在java中实现类还可以实现多个接口以弥补类只能单继承的缺陷,例如还有一个接口Singable,里面包含一个sing方法,此时实现类除了实现Flyable接口外还能实现Singable接口,但是实现类必须对所有接口的方法进行实现,如下:

接口2(Singable):

interface Singable{
    
    public void sing();
}

实现类(Bird.java)

class Bird implements Flyable,Singable{
    
    public void fly(){
        System.out.println("自由自在翱翔天际!");
    }
    
    public void sing(){
        System.out.println("你是我的小呀小苹果~~");
    }
}

接口与抽象类的区别

  1. 接口是一种更纯粹的抽象结构

  2. 接口是对行为的抽象(接口描述动词),抽象类是对一些类型的抽象(抽象类描述名词)

  3. 一个类只能存在一个直接父类,但是类可以实现多个接口(实现类必须实现所有的方法)

  4. 接口可以继承接口,并且能同时继承多个接口,抽象类只能直接继承一个抽象类

  5. JDK8允许接口中出现默认方法

内部类

java中可以在一个类的内部定义属于该类的从属类,从属类具备访问外部类的成员的能力,内部类提供了比方法更完整封装性,内部类主要包含以下几种:

  1. 成员内部类

  2. 局部内部类

  3. 静态内部类

  4. 匿名内部类

成员内部类

public class Outter {
​
    private String name = "旺财";
    
    public void m1(){
        InnerClass1 ic = new InnerClass1();
        System.out.println("获取内部类的成员变量:"+ic.name2);
    }
    
    /**
     * 成员内部类
     * @author mrchai
     *
     */
    private class InnerClass1{
        private String name2 = "来福";
        
        public void m2(){
            //内部类可以直接使用外部类的成员变量与方法
            System.out.println("成员内部类的方法:"+name);
            Outter.this.m1();
        }
        
        public void m1(){
            System.out.println("内部类的m1方法");
        }
    }
    
    public static void main(String[] args) {
        Outter.InnerClass1 in = new Outter().new InnerClass1();
        in.m2();
    }
}

局部内部类

public class Outter2 {
​
    private String name = "旺财";
    
    public void m(){
        //局部变量如果在内部类中使用则必须使用final修饰
        final int i = 10;
        //局部内部类不允许使用访问修饰符修饰
        class Inner{
            public void m2(){
                System.out.println("hello,局部内部类:"+i);
            }
        }
        //局部内部类的访问仅限于声明区域
        new Inner().m2();
    }
    
}

静态内部类

public class Outer3 {
​
    private String name1 = "旺财";
    
    static class Inner{
        String name2 = "来福";
        
        public void m(){
            System.out.println("外部类的成员变量:"+new Outer3().name1);
        }
    }
    
    public void m2(){
        new Inner().m();
    }
    
    public static void main(String[] args) {
        Outer3.Inner in = new Outer3.Inner();
        in.m();
    }
}
​

匿名内部类

匿名内部类顾名思义,即没有名字的内部类,一般表现形式为使用父类的引用指向你一个匿名对象,方法的实现在匿名语句块中完成

Animal a = new Animal() {
    @Override
    public void sleep() {
        System.out.println("休眠一下");
    }
};
//输出地址非Animal对象,而是包含了“$”符号的对象地址(即内部类)
System.out.println(a);
a.sleep();
​
Cat c = new Cat(){
    @Override
    public void eat() {
        System.out.println("吃猫粮");
    }
};
/*
针对以下接口创建匿名内部类对象
interface Sportable{
    void run();
    void jump();
}
*/

匿名内部类与回调机制

Button.java

public class Button {
​
    //内部接口
    public interface OnClickListener{
        //回调函数(钩子函数)
        void click();
    } 
    
    public void setOnClickListener(OnClickListener lis){
        System.out.println("准备触发");
        lis.click();
        System.out.println("清理资源");
    }
}

ButtonTest.java

public class ButtonTest{
    public static void main(String[] args) {
​
        Button btn = new Button();
        //匿名内部类使用
        btn.setOnClickListener(new OnClickListener() {
            //回调机制
            @Override
            public void click() {
                System.out.println("事件被触发。。。。");
            }
        });
    }
}

关于内部类注意事项

  1. 内部类可以使用外部类的成员变量和方法

  2. 局部内部类如果需要使用外部方法(或者语句块)的局部变量,该局部变量必须使用final修饰(延长局部变量的生命周期)

  3. 局部内部类的使用范围仅限于声明区域

  4. 静态内部类中不能直接使用外部类的实例方法实例变量,创建外部类对象,才能通过对象调用

异常处理&常用类

  • 异常概述

  • 异常分类

  • 异常处理

  • 自定义异常

  • Math类与Random类

异常概述

在java中程序的运行往往会因为设计或者编写过程中引起一些错误的操作,这些错误信息主要包含两种类型

  1. 错误(Error):通常是JVM内部错误,或者资源耗尽等一些无法从本质上解决的问题(严重问题)

  2. 异常(Exception):因为一些编程错误或者外在因素引起的可以被修复的问题

Error和Exception都是从Throwable类继承过来

异常分类(Exception)

java中的异常主要包含两种类型:

  1. 运行时异常(RuntimeException):在程序运行时才会出现

    1. java.lang.NullPointerException<空指针异常>

    2. java.lang.IndexOutOfBoundsException< 索引超出范围>

    3. java.lang.ArrayIndexOutOfBoundsException<数组索引越界>

    4. java.lang.NumberFormatException<转换为数值类型异常>

    5. java.lang.ClassCastException <类型转换异常>

    6. java.lang.ArithmeticException <算数异常>

  2. 一般异常(检查异常):在编译期就显式的通知程序员必须处理

    1. java.lang.ClassNotFoundException<类未找到异常>

    2. java.io.IOException<IO异常>

异常处理

java程序在运行时或者编译时如果出现异常,则会产生异常对象,对于该异常对象的处理方法,通常包含两种解决方法:

  1. 异常抛出(throws)

  2. 异常捕获(try...catch...fianlly)

java中的异常处理通常包含以下关键字:

  • throw :一定会抛出该异常,通常出现在方法内部用于显式抛出

  • throws :抛出有可能出现的异常,出现在方法声明区域(在参数列表后面)

  • try :try语句块一般用于将有可能出现异常的语句包裹

  • catch:处理try语句块中可能出现的异常,一旦异常产生则进入对应的catch块(可以catch多种异常)

  • finally:finally语句块中内容无论是否出现异常,都会执行,一般用执行资源清理与回收

思考以下方法执行之后i的结果?

public static int  m2(){
    int i = 10;
    try{
        i = 20;
        //System.out.println(i/0);
        return i;
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        i = 30;
        System.out.println("执行finally");
    }
    return i;
}

finally一般常用于,文件资源的回收,网络资源的关闭,数据库连接相关资源的回收

思考:java是否存在内存泄漏问题?

理论上java不存在内存泄漏,因为,java中存在GC机制,jvm会在适当的时候对申请内存空间释放;但是对于第三方的资源(文件,网络,数据库)访问GC无法自动关闭,通常都需要由程序员手动关闭或者释放,如果这些资源没有正常回收就有可能引起系统内存泄漏,通常对于以上资源的清理都放在finally处理。

自定义异常

在实际项目开发中,jdk自带的异常无法完全满足项目中的业务需求,此时可以通过对Exception类或者其子类继承,从而扩展自定义的异常类,比如有以下需求:

完成一个银行账户的转账逻辑,有账户A,B,此时账户A若向账户B转出一定金额,如果A账户余额不足则要抛出一个异常,但是JDK中并未包含该含义的异常类,因此可以自定义异常:LessMoneyException,如下:

LessMoneyException.java

public class LessMoneyException extends Exception{
​
    public LessMoneyException() {
    }
    
    public LessMoneyException(String msg){
        super(msg);
    }
}
​

自定义异常一般对Exception类或其子类继承后需要定义默认构造器,以及包含异常信息(String参数)的构造器

Account.java

public class Account {
​
    private double cash;
    
    public Account(double cash) {
        this.cash = cash;
    }
    
    /**
    * 执行转账逻辑,并抛出有可能出现的余额不足异常
    */
    public void changeOut(Account c,double money) throws LessMoneyException{
        if(this.cash >= money){
            //目标账号金额增加
            c.cash = c.cash + money;
            //当前账户金额减少
            this.cash = this.cash - money;
        }else{
            throw new LessMoneyException("骚年,余额不足,请充值!!!");
        }
    }
}
​

关于异常处理

  1. 对于异常抛出,被抛出的异常必须由后续的调用者处理(继续抛出,捕获)

  2. 存在继承关系的子类中不能抛出超出父类抛出的异常范围

  3. 如果父类方法没有抛出任何异常,则子类重写父类方法时只能对产生异常捕获

如何快速定位异常出现的位置:

  • 观察异常的类型名称

  • 观察异常的关键字(异常名之后)

  • 第一次出现异常的代码行数

常用类

  • Math&Random

  • File

  • Date&DateFormat

  • BigDecimal&NumberFormat

Math类&Random类

Math类在Java中常用于数学运算相关操作,例如,绝对值,三角函数,比较大小等,Math类中所有方法都是static方法,因此直接通过类名称调用即可

Math常用方法:

Math.abs(-100); //取绝对值:100              
Math.max(10,15);//返回两个数中较大的一个:15
Math.min(10,15);//返回两个数中较小的一个:10
Math.ceil(3.14);//向上取整:4
Math.floor(3.14);//向下取整:3
Math.round(3.14);//四舍五入:3
Math.pow(m,n);//计算获取m的n次幂
Math.random();//获取0.0~1.0(不含)之间的随机数

Random类用于生成伪随机数流,通过random可以随机生成不同数据类型数据,例如:整数,浮点数,布尔值等

Random示例代码:

Random r = new Random();
int i = r.nextInt(10);//随机获取从0-9之间的整数
boolean b = r.nextBoolean();//随机获取boolean值

File类

Java中File类用于表示一个标准文件或者目录对象,通过File提供的构造器和方法可以方便的操作本机磁盘中 的文件与目录,File相关示例代码:

//返回与系统相关的路径分隔符
System.out.println(File.pathSeparator);
System.out.println(File.pathSeparatorChar);
//返回与系统有关的目录分隔符 window:\    Linux:/
System.out.println(File.separator);
System.out.println("d:/test"+File.separator+"t.txt");
System.out.println("d:/test/t.txt");
​
//根据给定文件或目录的绝对或者相对路径创建File对象
File file = new File("D:/fileTest");
//删除指定的File对象,如果file对象表示的是目录且目录中无子文件或子目录则可以删除,对于标准文件可以直接删除
System.out.println(file.delete());
​
​
file = new File(file,"test/新建文本文档.txt");
//判断当前file对象表示的文件或目录是否存在
System.out.println(file.exists());
​
file = new File("d:/fileTest","test/新建文本文档.txt");
System.out.println(file);
​
file = new File("d:/fileTest/hello");
//判断指定File对象所表示的目录或者文件是否存在
if(!file.exists()){
    //创建当前文件(标准文件)
    System.out.println(file.createNewFile());
}
file = new File("d:/fileTest/java/project/myfiles");
//创建指定file对象所表示的目录
System.out.println( "目录创建结果:"+file.mkdir());
//创建指定file对象所表示的目录(可以创建多级目录)
System.out.println("多级目录创建:"+file.mkdirs());
/**************************************************/
File file = new File("D:\\fileTest\\helloworld.txt");
//立即删除文件
//      file.delete();
file.deleteOnExit();
System.out.println("运行中...");
//      Thread.sleep(10000);
System.out.println("结束");
​
file = new File("D:\\fileTest\\java\\project\\myfiles\\hello.txt");
//获取当前文件对象所在磁盘的剩余空间(字节)
System.out.println(file.getFreeSpace());
//获取File对象所表示的文件或者目录名称
System.out.println(file.getName());
//获取指定file对象所在的父路径(字符串)
System.out.println(file.getParent());
//获取指定file对象所在的父路径(File对象)
System.out.println(file.getParentFile());
//获取File对象表示的文件的绝对路径
System.out.println(file.getPath());
//获取当前File对象所表示文件所在磁盘的总空间
System.out.println(file.getTotalSpace());
​
file = new File("src/com/softeem/file/HelloWorld.java");
file.createNewFile();
//判断当前File所表示的文件是否是绝对路径
System.out.println(file.isAbsolute());
//判断当前File对象表示的是否是一个目录
System.out.println("是否是目录:"+file.isDirectory());        
//判断当前File对象表示的是否是一个标准文件
System.out.println("是否是标准文件:"+file.isFile());
​
System.out.println(System.currentTimeMillis());
System.out.println("文件的最近一次修改时间:"+file.lastModified());
//获取文件的大小(字节)
System.out.println(file.length());

续:

File file = new File("D:\\fileTest\\helloworld.txt");
//立即删除文件
//      file.delete();
//当jvm结束时删除
file.deleteOnExit();
System.out.println("运行中...");
//      Thread.sleep(10000);
System.out.println("结束");
​
file = new File("D:\\fileTest\\java\\project\\myfiles\\hello.txt");
//获取当前文件对象所在磁盘的剩余空间(字节)
System.out.println(file.getFreeSpace());
//获取File对象所表示的文件或者目录名称
System.out.println(file.getName());
//获取指定file对象所在的父路径(字符串)
System.out.println(file.getParent());
//获取指定file对象所在的父路径(File对象)
System.out.println(file.getParentFile());
//获取File对象表示的文件的绝对路径
System.out.println(file.getPath());
//获取当前File对象所表示文件所在磁盘的总空间
System.out.println(file.getTotalSpace());
​
file = new File("src/com/softeem/file/HelloWorld.java");
file.createNewFile();
//判断当前File所表示的文件是否是绝对路径
System.out.println(file.isAbsolute());
//判断当前File对象表示的是否是一个目录
System.out.println("是否是目录:"+file.isDirectory());        
//判断当前File对象表示的是否是一个标准文件
System.out.println("是否是标准文件:"+file.isFile());
​
System.out.println(System.currentTimeMillis());
System.out.println("文件的最近一次修改时间:"+file.lastModified());
//获取文件的大小(字节)
System.out.println(file.length());

File类之文件过滤(FileFilter)

FileFilter是一个用于完成文件过滤的接口,FileFilter接口提供了一个用于社子和过滤规则的抽象方法,若在获取文件列表时需要进行过滤,则可提供FileFilter对象并实现accept方法以完成文件过滤,如下

//创建File对象,表示d盘下的fileTest目录
File file = new File("d:/fileTest");
//过滤所有的标准文件(匿名内部类)
files = file.listFiles(new FileFilter() {
    @Override
    public boolean accept(File file) {
        return file.isFile();
    }
});

文件过滤原理:

通过查看 listFiles(FileFilter filter)方法源码不难发现,文件过滤原则即从文件列表中遍历所有文件,并根据accept实现的过滤规则判断每一个文件是否满足过滤条件,若满足则将该文件存储到新的容器ArrayList中,最终将ArrayList以File数组的形式返回,即完成过滤过程,源码如下:

public File[] listFiles(FileFilter arg0) {
    //获取目标文件的文件名数组
    String[] arg1 = this.list();
    if (arg1 == null) {
        return null;
    } else {
        //创建新数组用于存储符合过滤条件的文件
        ArrayList arg2 = new ArrayList();
        String[] arg3 = arg1;
        //获取所有文件总数
        int arg4 = arg1.length;
        //循环遍历每一个文件
        for (int arg5 = 0; arg5 < arg4; ++arg5) {
            //取出每一个遍历到的文件名并使用临时变量存储
            String arg6 = arg3[arg5];
            //根据文件名构建一个新的文件对象
            File arg7 = new File(arg6, this);
            //判断新文件对象是否符合accept方法实现的过滤规则
            if (arg0 == null || arg0.accept(arg7)) {
                //若符合过滤规则则将该文件加入新集合中
                arg2.add(arg7);
            }
        }
        //将集合转换为File数组并返回,完成过滤
        return (File[]) arg2.toArray(new File[arg2.size()]);
    }
}

Date类与DateFormat

java中可以使用java.util.Date表示一个日期对象,Date目前仅提供两个未过时的构造器完成对象创建,其余均标记为已过时(参考java.util.Calendar),并且Date类中大多数方法也已标记为过时,不推荐使用(参考java.util.Calendar类)

//根据当前系统时间创建Date对象
Date date = new Date();
//根据提供的时间毫秒数创建Date对象
date = new Date(System.currentTimeMillis())
//获取date对象表示的时间毫秒数(从1970年01月01日 00:00:00开始
long time = date.getTime();

由于Date类型默认的输出格式为欧美日期格式:Fri Jul 20 15:33:37 CST 2018;在国内环境下难以理解,因此,jdk也提供了用于对日期进行格式化处理的类DateFormat和SimpleDateFormat(DateFormat的子类),我们可以根据需求个性化的定制时间显示格式,DateFormat内部包含了部分方法用于格式化日期或者日期时间,如果需要定制则可以通过SimpleDateFormat实现,具体示例代码如下:

//获取当前系统时间对象
Date d = new Date();
//获取日期格式化对象
DateFormat fmt = DateFormat.getDateInstance();
fmt.format(d); //2018-7-20
​
//获取日期时间格式化对象
fmt = DateFormat.getDateTimeInstance();
fmt.format(d);//2018-7-20 16:10:11
​
//若需要将显示的时间个性化处理如:2018年07月20日 16时10分11秒 则可通过SimpleDateFormat实现
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
sdf.format(d);//2018年07月20日 16时10分11秒
​
​
//如何将字符串类型日期转换为Date类型对象?
String t1 = "2017/08/01 12:12:12";
String t2 = "2017/07/01 12:12:12";
​
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//将字符串类型的日期解析为Date类型
Date d1 = sdf.parse(t1);
Date d2 = sdf.parse(t2);

BigDecimal&NumberFormat使用

java中进行浮点数运算时由于计算机底层运算问题可能导致精度丢失,例如:

double d1 = 0.3;
double d2 = 0.2;
​
System.out.println(d1 - d2);//0.199999999999998

针对以上算术运算预期值应该是:0.2,但是实际结果确是:0.19999999999999998;如果用于表示金额等敏感数值则可能引起极大错误,因此对于精度要求较高的浮点数运算时,通常使用BigDecimal类型处理,具体如下:

BigDecimal d1 = new BigDecimal("0.3");
BigDecimal d2 = new BigDecimal("0.1");
​
//相加
BigDecimal add = d1.add(d2));
//相减
BigDecimal subtract = d1.subtract(d2);
//相乘
BigDecimal multiply = d1.multiply(d2);
//相除
BigDecimal div = d1.divide(d2);
//取余
System.out.println(d1.remainder(d2));
​
/*针对以上相除运算如果反过来使用d2处以d1则会导致小数点后无穷大,从而引发ArithmeticException异常,
 因此针对以上问题,应该采用
    divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
    divisor      表示除数
    scale        精度(小数点后保留位数)
    roundingMode 舍入模式
 方法设置数值精度来避免异常,如下:
*/
BigDecimal div2 = d2.divide(d1,2,RoundingMode.HALF_UP);
​
//针对以上结果若想转化为其他基本数据类型则可使用以下方法解决:
//div2.intValue()
//div2.doubleValue()
//...

在表示一个数值时通常根据项目的不同需求可能需要显示为不同的格式,比如:货币,百分比等,此时可以通过NumberFormat&DecimalFormat进行解决,如下:

double d = 432765123.89653;
//使用标准显示格式
String s = NumberFormat.getInstance().format(d);//结果:432,765,123.897
//显示为货币格式
s = NumberFormat.getCurrencyInstance().format(d);//结果:¥432,765,123.897
​
d = 0.5456;
//显示为百分比
s = NumberFormat.getPercentInstance().format(d);//结果:54%

以上为默认的数值格式化工具,但是针对某些特定需求(比如数值d要求保留小数点后两位,默认为3位;百分比值要求保留一位小数,默认取整),默认的格式化工具可能无法完全满足需求,此时可以通过DecimalFormat定制个性化需求:

double d = 432765123.88653;
DecimalFormat fmt = new DecimalFormat("##,###.##");
fmt.format(d);//结果:432,765,123.89
​
double d = 0.5456
DecimalFormat fmt = new DecimalFormat("##.#%");
fmt.format(d);//结果:54.6%

练习

  1. 完成一个验证码生成器,验证码生成器要求能随机生成四位包含数字和字母的字符串,并完成验证过程

  2. 思考如何遍历一个目录,取出目录中的所有子文件和子目录,如果存在子目录能否将子目录中的子文件取出(目录递归读取)

  3. 读取一个目录,显示该目录中的子文件相关信息(文件名,类型,创建时间,大小)

  4. 完成一个斗地主的发牌系统

    • A-K 一共52张,以及大王和小王共54张,

    • 要求每张牌具备不同花色(大小王除外),花色:红心,黑桃,方块,梅花

    • 将54张牌存储到ArrayList中

    • 从3名玩家中随机产生一名地主

    • 按照顺序起牌(A-B-C-A-B-C-A...),最后剩余3张归地主

    • 对每位玩家手中起到的牌排序处理(集合排序)

常用类

  • Calendar类

  • Timer&TimerTask

  • 正则表达式

Calendar类

从JDK1.1开始,Java便提供了比java.util.Date更为全面的日期处理类java.util.Calendar,Calendar类是一个抽象类,其子类为GregorianCalendar,同时Calendar也提供了静态方法getInstance()用于返回Calendar实例:

//根据当前的语言地区环境获取一个日历对象
Calendar c = Calendar.getInstance();
System.out.println(c);
​
System.out.println("获取年份:"+c.get(Calendar.YEAR));
System.out.println("获取月份:"+(c.get(Calendar.MONTH)+1));//月份0~11
System.out.println("获取日期(今天是该月的第几天):"+c.get(Calendar.DAY_OF_MONTH));
System.out.println("获取小时数(12小时制):"+c.get(Calendar.HOUR));
System.out.println("获取小时数(24小时制):"+c.get(Calendar.HOUR_OF_DAY));
System.out.println("获取分钟数:"+c.get(Calendar.MINUTE));
System.out.println("获取秒钟数:"+c.get(Calendar.SECOND));
System.out.println("获取毫秒数:"+c.get(Calendar.MILLISECOND));
System.out.println("获取当前是该年中的第几天:"+c.get(Calendar.DAY_OF_YEAR));
System.out.println("获取今天是周几:"+c.get(Calendar.DAY_OF_WEEK));
​
//将月份设置到6月份
c.set(Calendar.MONTH, 5);
//如何获取该月的总天数与该月第一天的周数?
//获取今年的总天数
System.out.println(c.getActualMaximum(Calendar.DAY_OF_YEAR));
//获取当前日历表示的该月份的最大天数(总天数)
System.out.println(c.getActualMaximum(Calendar.DAY_OF_MONTH));
//获取该月的第一天是周几?
//将日期设置到这个月的第一天
c.set(Calendar.DAY_OF_MONTH, 1);
System.out.println("获取今天是周几:"+c.get(Calendar.DAY_OF_WEEK));

Timer&TimerTask

正则表达式(Regex)

正则表达式类似于操作系统的一些通配符,通过特定的符号组合的表达式对一段文本进行检索,替换,分析等操作,正则表达式最早起源于Perl语言,目前成为业内的一种通用的表达式语言,Java中通常基于以下几个类使用正则表达式:

  • String

  • Pattern(正则表达式编译器)

  • Matcher(匹配器)

猜你喜欢

转载自blog.csdn.net/qq_37693860/article/details/81430562