iOS开发者的Java学习笔记


iOS开发者的Java学习笔记


Java logo


本篇笔记主要记录了JavaSE的学习。

目录

  • Java概述
  • Java数组、字符串和枚举
  • 类与对象
  • 面向对象
  • 四种内部类
  • 多线程
  • 正则表达式
  • 反射机制
  • 类集框架

Java概述:


Java分为三个体系:
  • JavaSE(Java Platform Standard Edition即Java平台标准版)
  • JavaEE(Java Platform Enterprise Edition即Java平台企业版)
  • JavaME(Java Platform Micro Edition即Java平台微型版)

本篇笔记主要记述了JavaSE;

Java跨平台介绍:

关于Java这门编程语言,在初次接触时变听闻其具有“一次编写,到处运行”的特点,之前学习iOS开发时、必备Mac OS系统或是黑苹果。

Java这个优势得益于JVM(Java虚拟机)的存在。当我们完成了编写源程序(.java文件)之后,编译程序可以得到字节码文件(.class)。不同的平台有不同的JVM,JVM将字节码文件翻译成平台能识别的机器语言。

Java的对象

学过C语言的同学都知道,需要用指针指向堆中的变量。对于Objective-C就更厉害了,所有对象皆为指针(所有的对象皆被指针变量所引用)。而在Java中没有指针的概念、只有引用的概念,所有的参数赋值皆为值传递(基本类型值传递和引用值传递都是一个值拷贝的过程,所以皆称为值传递)。而在C++的说法中既有传值调用(基本类型值传递)又有传址调用,而传址调用又有引用和指针的概念。


Java数组与字符串


Java一维数组:
        //这是一个保存了基本类型变量的数组
        1int[] array1 = { 3, 5, 7, 9, 77, 95 };

        //一维数组的另两种定义方式
//      2、int[] array = new int[]{ 3, 5, 7, 9, 77, 95 };
//      3、int[] array3 = new int[5];


        //这是一个保存了对象(对象引用)的数组
        String s1 = new String("aaa");
        String s2 = new String("bbb");
        4、String[] array2 = { s1, s2 };

一般我们都会以上述方式声明一个一维数组.需要注意的是:

  • 基本类型数组直接保存的是基本类型的值,而对象数组保存的是引用:
    一维数组
  • 新生成的对象数组,其中的元素自动初始化为null;基本类型数组中的成员有各自的默认值(int型初始化为0,布尔型为false)。
  • 访问数组对象的成员只能使用array1[下标]这种方式。
Java二维数组:

先看Java的定义方式:

    1int[][] b = {{1,2},{3,4}};
    2int[][] a = new int[3][];

第二种数组创建的方式可能让人感到很诧异,因为我们在学习C语言的二维数组时清楚的记得是:行数可以省略,列数不可以省略。而在Java中却是列数可以省略,行数不可以省略。black question

我们先来对比一下C语言中和Java语言中数组的存储方式:在C语言中二维数组也是连续存放在栈中的,如下图:
一维数组
很明显,这种存储方式决定了C语言二维数组在定义时不能省略列数。

再来看一下在Java中数组是怎样存储的:
这里写图片描述
这就是Java中二维数组的存储结构,通过new int[3][]开辟了一个保存引用的数组,此时引用都指向null,可以通过以下代码再次开辟空间:

a[0] = new int[5];
a[1] = new int[4];
a[2] = new int[3];
Java字符串

每一种编程语言的字符串操作都是非常重要的。
Java中提供了三种字符串类:String、StringBuilder和StringBuffer:

String

String对象是不可变的即不可被修改:

String s1 = "aaa";
s1 = "bbb";

第一行代码声明了一个字符串的引用变量s1,和一个字符串对象“bbb”,第二行看似修改了字符串对象,只是s1引用了不同的字符串对象而已,“aaa”还是那个“aaa”并没有被修改。

String对象有两种创建方式

        1String s1 = "aaa";
        2String s2 = "aaa";
        3String s3 = new String("aaa");
        4String s4 = new String("aaa");

这里写图片描述
字符串字面值存储于字符串池中,且不会存在重复的字符串实例,这也是我们优先使用的一种方式。使用new生成的字符串对象存在于堆中,如图示。

为什么将String设计为不可变?

只读的特性是为了提高效率。如果对字符串修改的效率很高,就需要使用另外两个类了。

StringBuilder和StringBuffer

StringBuffer对象是线程安全的可变字符序列,StringBuilder是单线程使用的可变字符序列,除线程问题外二者等价。

通常优先使用StringBuilder类,因为它不需要执行同步操作,所以效率更高。

枚举

Java中的枚举是非常强大的,事实上其定义的类型就是一个类

enum Color {
//  枚举类型的值必须作为第一条语句出现
    RED,
    BLUE,
    YELLOW;
    private Color() {
        System.out.println("construter");
    }
}

枚举值都是public static final类型的常量
枚举的构造器必须是私有的,不允许定义为public,构造器在第一次使用枚举值的时候被调用,有几个枚举值就调用多少次

switch

Java中,switch表达式的case语句也是非常强大的,在Java5前只允许case的类型有:byte、short、int、char(long、double都不可以,因为可能会造成精度丢失),Java5中加入了枚举,Java7中加入了string


Java类与对象


UML类图:

这里写图片描述

package

标准Java库是由一系列包组成,比如程序默认为我们引入的java.lang包,还有工具包java.util等等等。通过打包可以有效的防止命名冲突,同时也就有了默认访问控制权限。

我们使用package声明一个包,使用import导入一个包(或者在类前面加上完整的包名)。为了使代码具有层次性,我们开发中会编写很多包,包名必须具有唯一性,且由小写字母组成,一般使用域名的倒置作为包的唯一前缀,如:com.afei.demo。

一个类可以使用同一个包中的所有类,和其它包中的公开类。而在一个包中,只有一个公开类,而且该类名与文件名一致。

访问权限修饰符

这里写图片描述

构造方法:
  1. 构造方法负责初始化对象,为对象的属性赋值。
  2. 构造方法名与类名相同,没有返回值。
  3. Java系统保证每个类都有构造方法,如果一个类没有显式声明构造方法时,系统默认会有一个无参构造方法,若显式创建了有参构造方法,则无此无参构造方法。
this关键字
  1. 在类的实例方法中使用this关键字代表调用此方法的对象;
  2. 使用this可以处理方法中的成员变量与形参同名问题
  3. 在类的构造方法中可以使用this(参数列表)来调用该类的指定构造器方法,但是该语句必须是所在构造器的第一条语句.
public Person() {
        this("afei", "man", 0.00);
    }
    public Person(String name, String sex, double money) {
        this.name = name;
        this.sex = sex;
        this.money = money;
    }
static关键字
  1. 使用static修饰的成员变量称为类变量也称为静态变量;修饰的成员方法称为类方法也称为静态方法。
  2. 静态变量和静态方法优先于对象的存在,在类被加载的时候就会被加载,此时我们还未创建对象。它们属于类,而不属于某个对象。
  3. 静态成员的初始化可以放在静态代码块中执行,静态代码块只会执行一次。
  4. 调用时最好的方式是直接通过类名.成员来调用,因为它们属于类而不是实例。
  5. 静态方法不能出现this关键字,因为该方法被加载的时候还不存在实例,所以静态方法只能访问外部的静态成员。
方法重载(over load)

方法重载:同一个类中,方法名相同、参数列表不同的方法同时存在称为方法重载。在Objective-C中不允许方法重载,因为它要求方法名是唯一的。而在C++和Java中解析方法的规则是方法签名(方法名+参数列表),而不是方法名。所以可以进行方法重载,且C++和Java的方法重载的要求是一致的。

方法重载要求方法名一致,参数列表不一致(参数个数不一致或参数类型不一致或二者都不一致),仅有返回类型不同的方法不能称为重载(方法签名相同就无法决定所要调用的方法)。


面向对象


继承(extends)

继承(extends):所谓继承就是基于已存在的类来构建新类,通过继承实现复用。子类还可以为自己添加新的属性和方法来完善自己。C++是允许多继承的,而Java和Objective-C是单继承的语言。所谓单继承指的是:每个类只允许直接继承一个类。

继承体现的是is-a关系。

我们所创建的类若不显式继承别的类,则默认继承Object类。

super关键字
  1. 通过super可以调用父类的成员变量和构造方法。
  2. 在子类的构造方法中,通过super(参数列表)调用父类的构造方法,而且这条语句必须是子类构造方法中的第一条指令。
instanceof运算符

instanceof运算符是用来判断:一个对象是否是特定类或者其子类的一个实例,若是则返回true,否则返回false。
对象进行向下转型之前(下面多态是介绍),可以使用该运算符进行判断以避免抛出异常。

方法重写(over ride)

方法重写:子类中可以对父类的方法进行改写。重写的方法和父类的方法名称、参数列表、返回值必须完全一致。

重写方法的访问权限可以和父类被重写方法的权限不一致,但是不能使用比父类被重写方法更严格的访问权限

父类中的私有方法不能被重写

如果只是对父类的方法进行扩充,可以使用super.函数名调用父类的方法。

final关键字
  1. 被final修饰的变量就变成了一个常量。
  2. 被final修饰的方法不能被重写。
  3. 被fianl修饰的类不能被继承。
抽象类

抽象:对于一个事物,将我们所关注的内容提取出来。

抽象方法:只有方法头没有方法体的方法称为抽象方法;抽象方法使用abstract来修饰。

抽象类:使用abstract修饰类称为抽象类。抽象类不能被实例化。抽象类中可以有具体方法可以没有抽象方法

一个类若继承了一个抽象类,要么实现抽象类中的抽象方法,要么继续作为一个抽象类。

接口(interface)

接口定义了不相关的类之间的共同特征,接口不关注类之间的关系、只是抽取这些类所共同具备的属性和行为。

而抽象类与实现类则是一种被继承关系。

一个类可以继承一个类,并且同时实现多个接口(单继承,多实现).

接口与接口之间可以使用extends继承

在Java8之前,接口中只能存放静态常量和抽象方法。在Java8出来后,接口有了很大变化,增加了static方法:由接口直接调用,还增加了default方法:所有继承该接口的类都具有的方法。

    public static final int number = 1;
    //  public int number = 1;可以这样简写,依然表示静态常量
    public abstract void eat(); 
    //  public void eat();可以这样简写
    default void defaultMethod() {
        System.out.println("继承该接口的类都继承了该方法");
    }
    static void staticMethod() {
        System.out.println("由接口直接调用");
    }
} 
多态

多态:同类对象接收相同消息产生不同的行为。

多态产生的三个必要条件:

  1. 需要存在继承
  2. 需要有方法重写
  3. 向上转型:父类或接口的引用变量引用子类对象

多态的作用:

  1. 将接口和实现分离
  2. 消除类型之间的耦合
  3. 提高程序的扩展性和可维护

向上转型是安全的,不需要强制类型转换,但在转型之后,只能调用父类或接口中存在的方法,这就可能造成子类自己扩展的方法丢失。

与向上转型相反的是向下转型,这是不安全的,需要进行强制类型转换(同时最好使用instanceof来加以判断)。

静态绑定和动态绑定

绑定:一个方法的调用与方法所在的类关联起来,Java中分为静态绑定(前期绑定)和动态绑定(后期绑定)。

静态绑定:在程序执行前方法就已经被绑定,也就是编译时期的绑定。Java中只有final,static,private和构造方法是静态绑定的。

动态绑定:在运行时根据运行时对象的类型进行绑定。下面测试代码使用了多态,a.eat()就是动态绑定,最终调用的是Dod类的eat()方法。

    abstract class Animal {
        public abstract void eat();
    }
    class Dog extends Animal {
        private String name = "dog";
        public Dog(String name) {
            this.name = name;
        }
        public void eat() {
            System.out.println("dog eat");
        }
    }
    Animal a = new Dog("a");
    a.eat();
自动装箱与自动拆箱

有时候我们需要将int这样的基本类型转换为引用类型对象,于是出现了自动装箱。
Integer num = 1;这行代码等价于Integer num = new Integer(1);

既然有了自动装箱也就有自动拆箱
int numInt = num;这句代码等价于int numInt = num.intValue();

Java垃圾回收机制

我们在编写程序时,有些对象将不再被任何变量引用,如作用域使引用变量废弃或人为将对象置为null,如此我们将再也访问不到堆中的对象,若堆中的对象不被及时处理将造成堆内存告急。在Java运行时系统中有一个线程负责清除不再被使用的对象即垃圾回收器。

垃圾回收器会定期扫描内存,对于被使用的对象加上标记,未加标记的对象则会被清除。


四种内部类


内部类:一个类可以定义在另一个类的内部,该类称为内部类。Java中提供了四种内部类:成员内部类、静态内部类、匿名内部类和局部内部类。

成员内部类
  1. 成员内部类属于外部类的实例的成员。可以像成员变量和成员方法一样使用public、private、protected和default权限修饰符修饰。
public class ChengYuanNeiBuLei {

    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.showInner();
    }

}
class Outer {
    private String name = "outer";
    private int num = 10;
    public void showOuter() {
        System.out.println(name + num);
    }
    public class Inner {
        private String name = "inner";
        private int num2 = 20;
        public void showInner() {
            System.out.println(name + num2);
            System.out.println(name + Outer.this.name);
        }
    }
}

2、如上述代码,需要注意的几点是:

  • 在外部创建时:首先要创建外部类的实例才能创建内部类的实例;创建的语法是:Outer.Inner inner = outer.new Inner();
  • 在成员内部类中访问外部类的成员方法和成员变量时使用的语法是:Outer.this.成员方法or成员变量
  • 成员内部类不能与外部类重名
  • 不允许在成员内部类中定义static属性(static final形式的常量除外)、static方法和static类(静态内部类),因为这是没必要的。

成员内部类可以直接访问外部类的成员包括私有成员,外部类不能直接访问内部类的成员,只有先建立内部类的对象才可以访问。

成员内部类的出现可以间接的实现多继承,如下代码:

 class A {
    public void showA() {
        System.out.println("show A");
    }
}
class B {
    public void showB() {
        System.out.println("show B");
    }
}
class MulitiEstends {
    class A1 extends A {
    }
    class B1 extends B {
    }
    public void showA() {
        new A1().showA();
    }   
    public void showB() {
        new B1().showB();
    }
}

当同一个类中因继承或实现接口而出现了同名方法是,使用成员内部类可以避免修改方法,如下代码:

class C {
    public void show() {
        System.out.println("class show A");
    }
}
interface IA {
    public abstract void show();
}
class Content extends C {
    class Inner implements IA {
        public void show() {
            System.out.print("interface showa");
        }
    }
    public void show1() {
        new Inner().show();
    }
} 
静态内部类

静态内部类:使用static修饰的成员内部类叫静态内部类。

public class JingTaiNeiBuLei {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        inner.showInner();
        Outer outer = new Outer();
        outer.showOuter();
    }
}
 class Outer {
    private String name = "outr";
    private static int num = 1;
    public void showOuter() {
        System.out.println(name + num);
        System.out.println(Inner.num2 + new Inner().name);
    }
    public static class Inner {
        private String name = "Inner";
        private static int num2 = 2;
        public void showInner() {
            System.out.println(name + num + num2);
        }
    }
}

需要注意的几点:

  • 静态内部类的创建:Outer.Inner inner = new Outer.Inner();
  • 静态类的使用方式与外部类使用相同:inner.showInner();
  • 静态内部类不能与外部类重名
  • 静态内部类只能访问外部类的静态方法和静态成员变量,外部类也只能直接访问内部类的静态方法和静态成员变量(但可以new一个静态内部类对象之后访问其非静态的成员变量和方法,如上面的代码)
匿名内部类

匿名内部类:没有名称的内部类,既然是匿名的,就没有办法引用它。所以在创建时需要作为new语句的一部分来声明并创建它们的实例。
匿名内部类必须继承一个类,或者实现一个接口。
匿名内部类中不能定义静态代码块。
这里只是给出类匿名内部类的概念,具体的使用会在后面给出。

局部内部类
public class JuBuNeiBuLei {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.showOuter();
    }
}

class Outer {
    private int num1 = 1;
    private static int num2 = 2;
    public void showOuter() {
        int num3 = 3;
        final int num4 = 4;
        class Inner {
            private int num1 = 6;
            public void showInner() {
                System.out.println(num1);
                System.out.println(Outer.this.num1);
                System.out.println(num2);
                System.out.println(num4);
                System.out.println(num3);

            }
        }
        Inner inner = new Inner();
        inner.showInner();
    }
}

上面是具体的定义局部内部类的代码,可以看出局部内部类是定义在代码块、方法体内的类。
局部内部类访问外部类的属性和方法使用:Outer.this.num1,这种访问方式和成员内部类是一致的。
局部内部类不是类的成员,所以不能使用使用访问修饰符修饰。
成员内部类不能与外部类重名。


多线程


Java可以通过三种方式实现多线程:继承Thread类、实现Runnable接口、实现Callable接口

一、通过继承Thread类实现多线程,代码如下:

package ThreadDemo1;
class PrimeThread extends Thread {
    private long minPrime;
    public PrimeThread(long m) {
        minPrime = m;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i ++) {
            System.out.println(minPrime);
        }
    }
}
public class ThreadDemo1 {
    public static void main(String[] args) {
        PrimeThread p1 = new PrimeThread(1);
        PrimeThread p2 = new PrimeThread(2);
        PrimeThread p3 = new PrimeThread(3);
        p1.start();
        p2.start();
        p3.start();
    }
}
  • 继承Thread类,重写run方法
  • 为自己编写的多线程类创建对象,调用start()方法。

二、实现Runnable接口:

package ThreadDemo2;
class PrimeRun implements Runnable {
    private long minPrime;
    public PrimeRun(long m) {
        minPrime = m;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i ++) {
            System.out.println(minPrime);
        }
    }
}
public class ThreadDemo2 {
    public static void main(String[] args) {
        Runnable r1 = new PrimeRun(1);
        Runnable r2 = new PrimeRun(2);
        Runnable r3 = new PrimeRun(3);
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r3).start();
    }
}
  • 实现Runnable接口并重写run方法
  • 为自己编写的多线程类创建对象r
  • 以r为参数创建Thread的实例并调用start方法

三、实现Callable接口:

package ThreadDemo3;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<String> {
    private int ticket = 100;
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (ticket > 0) {
                System.out.println(this.ticket --);
            }
        }
        return "票已卖光";
    }
}
public class ThreadDemo3 {
    public static void main(String[] args) throws Exception {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        FutureTask<String> task1 = new FutureTask<String>(mt1);
        FutureTask<String> task2 = new FutureTask<String>(mt2);
        new Thread(task1).start();
        new Thread(task2).start();
        System.out.println(task1.get());
        System.out.println(task2.get());
    }
}
  • 实现Callable接口并且重写call方法,可以看到call方法时有返回值的
  • 为我们编写的多线程类创建对象r,并以r为参数创建FutureTask的实例f
  • 以f为参数创建Thread类的实例并调用start启动多线程
  • 通过f的get方法可以获取到方法返回值

Thread类是Runnable接口的子类。通过继承Runnable接口实现多线程比继承Thread类实现多线程更有优势,因为Java只允许单继承却可以多实现。但是run方法不能返回结果,如果需要返回结果的话就需要实现Callable,其使用起来比较麻烦。

线程同步

当多个线程同时访问同一个资源时,就要使用到线程同步。在Java中使用 synchronized关键字来实现,可以有两种方式:
* 使用同步代码块synchronized (对象){代码块}
* 使用同步方法(更加方便)

    package ThreadDemo4;
class MyThread implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {
        for(int i = 0; i < 5; i++) {
            sale();
        }
    }
    public synchronized void sale() {
        if (ticket > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ticket --);
        }
    }
}
public class ThreadDemo4 {
    public static void main(String[] args) {
        Runnable mt = new MyThread();
        new Thread(mt, "A").start();
        new Thread(mt, "B").start();
        new Thread(mt, "C").start();    
    }
}

但是同步方法和同步代码块可能造成死锁。

生产者与消费者案例
package ThreadDemo5;
class MacBookPro {
    public boolean flag = true;
    private String name;
    synchronized public void set(String n) {
        if (flag == false) {
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        name = n;
        flag =false;
        super.notify();
    }
    synchronized public void get() {
        if (flag == true) {
            try {
                super.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(name);
        flag = true;
        super.notify();
    }
}
class Producer implements Runnable{
    private MacBookPro mac;
    public Producer(MacBookPro m) {
        mac = m;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100;  i++) {

            if (i % 2 == 0) {
                mac.set("mac2017");
            } else {
                mac.set("mac2016");             
            }
        }
    }

}
class Consumer implements Runnable {
    private MacBookPro mac;
    public Consumer(MacBookPro m) {
        mac = m;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100;  i++) {

            mac.get();

        }
    }

}
public class ThreadDemo5 {

    public static void main(String[] args) {
        MacBookPro mac = new MacBookPro();
        Producer p = new Producer(mac);
        Consumer c = new Consumer(mac);
        new Thread(p).start();
        new Thread(c).start();
    }

}

wait()是Object类定义的方法,需要通过notify()方法唤醒
而sleep是Thread类的方法,需要设置休眠时间,自动唤醒


正则表达式


正则表达式的使用可以减少很多代码,本片只是记录了String对象对正则表达式的支持。

字符类
[abc]   ab 或 c(简单类)
[^abc]  任何字符,除了 ab 或 c(否定)
[a-zA-Z]    a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]]  a 到 d 或 m 到 p[a-dm-p](并集)
[a-z&&[def]]    d、e 或 f(交集)
[a-z&&[^bc]]    a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]]   a 到 z,而非 m 到 p[a-lq-z](减去)
.   任何字符(与行结束符可能匹配也可能不匹配)
\d  数字:[0-9]
\D  非数字: [^0-9]
\s  空白字符:[ \t\n\x0B\f\r]
\S  非空白字符:[^\s]
\w  单词字符:[a-zA-Z_0-9]
\W  非单词字符:[^\w]
Greedy 数量词
X?  X,一次或一次也没有
X*  X,零次或多次
X+  X,一次或多次
X{n}    X,恰好 n 次
X{n,}   X,至少 n 次
X{n,m}  X,至少 n 次,但是不超过 m 次
//      判断单词字符:[a-zA-Z_0-9]是不是出现了310次
        String s1 = "safas";
        String regex = "\\w{3,10}";
        System.out.println(s1.matches(regex));

//      保留s2中的所有小写字母
        String s2 = "rve$g543G4gvreg#rgfre";
        String regex2 = "[^a-z]";
        System.out.println(s2.replaceAll(regex2, ""));

//      去除字符串中的所有数字
        String s3 = "sd2vdsv222vds4dvs5";
        String regex3 = "\\d+";
        String[] resault = s3.split(regex3);
              System.out.println(Arrays.toString(resault));

//      判断字符串能否转换成double类型
        String s4 = "123";
        String regex4 = "\\d+(\\.\\d+)?";
        if (s4.matches(regex4)) {
//          将字符串转换成Double
            System.out.println(Double.parseDouble(s4));
        }

//      判断IP地址
        String s5 = "192.168.171.6";
        String regex5 = "(\\d{1,3}\\.){3}\\d{1,3}";
        System.out.println(s5.matches(regex5));

//      判断日期格式
        String s6 = "2017-10-31";
        String regex6 = "\\d{4}-\\d{2}-\\d{2}";
        if (s6.matches(regex6)) {
            try {
                Date date = new SimpleDateFormat("yyyy-MM-dd").parse(s6);
                System.out.println(date);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
//      判断电话号码
        String s7 = "12345678";
        String s77 = "010-1234567";
        String s777 = "(010)-12345678";
        String  regex7 = "((\\d{3,4}-)|(\\(\\d{3,4}\\)-))?\\d{7,8}";
//      String regex7 = "((\\d{3,4}-))?\\d{7,8}";
        System.out.println(s7.matches(regex7));
        System.out.println(s77.matches(regex7));
        System.out.println(s777.matches(regex7));

//      检测Email 
        String s8 = "w17915_676@qq.com";
        String regex8 = "\\w+@\\w+\\.\\w+";
        System.out.println(s8.matches(regex8));

//      检测Email  要求用户名由字母、数字、下划线 和. 组成,
//      必须以字母开头和 字母数字结尾,最后的根域名只能是:
//       .com .cn .net .con.cn .net.cn .edu .gov .org
        String regex9  = "[a-zA-Z][a-zA-Z0-9_\\.]{0,28}[a-zA-Z0-9]@\\w+\\.(net|cn|com\\.cn|com|org|gov|edu)";

        System.out.println(s8.matches(regex9));
        /**
         * 正则表达式中一定不可以由空格
         */

反射机制


JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。java.lang.Class类是反射操作的源头,所有的反射都要从此类开始进行

  • Class的三种实例化方式:
Date date = new Date();
        /**
         * 1
         * getClass是Object类中的方法
         * 用于返回实例所属的类
         */
        Class<?> cls = date.getClass();
        System.out.println(cls);

        /**
         * 2
         * 通过类.class取得
         */
        System.out.println(Date.class);

        /**
         * 3
         * 通过Class类的forName()方法
         * 此方法不需要导入明确的类,而是通过字符串来获得
         */
        Class<?> cls2 = Class.forName("java.util.Date");
        System.out.println(cls2);
  • 通过反射实例化对象
class Book {
    public Book() {
        System.out.println("___book's construct___");
    }
    @Override
    public String toString() {
        return "i am a book";
    }
}

public class LearnReflect2 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        /**
         * 使用反射实例化对象
         */
        Class<?> cls = Class.forName("LearnReflect1.Book");
        Object obj = cls.newInstance(); //相当于使用new调用无参构造方法
        Book book = (Book)obj;
        System.out.println(book);
    }

}
  • 通过上述方法只能调无参构造方法初始化对象,如果没有无参构造方法或者不想调用无参构造方法,可以这样写:
package LearnReflect1;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Iphone {
    private String name;
    public Iphone(String n) {
        name = n;
    }
    @Override
    public String toString() {
        return name;
    }
}


public class LearnReflect3 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        Class<?> cls = Class.forName("LearnReflect1.Iphone");
//      Iphone iphone = (Iphone)cls.newInstance(); //相当于调用无参构造方法,但是没有,所以应调用构造方法
        /**
         * 调用的构造方法需要传入参数,该参数是构造方法参数的参数类型
         */
        Constructor<?> con = cls.getConstructor(String.class);
        Object obj = con.newInstance("Iphone7");
        System.out.println(obj);
    }
}
  • 通过反射不仅能创建对象,而且能够调用普通方法和所有成员(包括私有成员)
package LearnReflect1;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Mac {
    private String name;
    private String test;
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

public class LearnReflect4 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
        /**
         * 通过反射调用普通方法:
         * Method getMethod(String name, Class<?>... parameterTypes)返回一个 Method 对象,
         * 它反映此 Class 对象所表示的类或接口的指定公共成员方法。
         * 
         * Method[] getMethods() 返回一个包含某些 Method 对象的数组,
         * 这些对象反映此 Class 对象所表示的类或接口
         * (包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
         */
        String name = "name";

        Class<?> cls = Class.forName("LearnReflect1.Mac");
        Object obj = cls.newInstance();
        Method setMethod = cls.getMethod("set" + initCap(name), String.class);
        Method getMethod = cls.getMethod("get" + initCap(name));
        setMethod.invoke(obj, "mac1799");
        System.out.println(getMethod.invoke(obj));


        /**
         * 反射调用成员(包括私有成员)
         * 获得全部成员:getDeclaredField(String name) 
         * 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
         * 
         * 获得指定成员:getDeclaredFields() 
         * 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
         */
        Field field = cls.getDeclaredField("test");
        field.setAccessible(true); //非常重要、取消封装
        field.set(obj, "test");
        System.out.println(field.get(obj));

    }
    public static String initCap(String s) {
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

}
  • 泛型类与泛型接口的定义:
//泛型接口
interface ITemp<T3> {
    void show();
}
//泛型类
class Template <T> {
    private T t;
    public Template(T t) {
        this.t= t;
    }
    public T getObject() {
        return this.t;
    }
}
  • 泛型类可以继承泛型类也可以起实现泛型接口:
class Template2 <T,T2,T3> extends Template<T> implements ITemp<T3> {
    private T2 t2;
    private T3 t3;
    public Template2(T t,T2 t2) {
        super(t);
        this.t2 = t2;
    }
    public Template2(T t,T2 t2,T3 t3) {
        super(t);
        this.t2 = t2;
        this.t3 = t3;
    }
    public T getObject() {
        return super.getObject();
    }
    public T2 getChildObject() {
        return t2;
    }
    @Override
    public void show() {
        System.out.println(this.t3);
    }
}
  • 可以对泛型做出限定:

public class TemplateDemo2 {

    public static void main(String[] args) {
        Cat c = new Cat();
        Template<Cat> t = new Template<Cat>(c);
        t.get().eat();


//      Template<Object> t1 = t;
//      无限定通配符
        Template<?> t1 = t;
        ((Cat)t1.get()).eat();

//      上边界限定通配符
        Template<? extends Animals> t2 = t1;
        t2.get().eat();

        Template<? super Cat> t3 = t; 
        t3.get().eat();
    }

}
abstract class Animals {
    public abstract void eat();
}

class Dog extends Animals {

    @Override
    public void eat() {
        System.out.println("Dog eat");
    }

}
class Cat extends Animals {

    @Override
    public void eat() {
        System.out.println("Cat eat");
    }

}
//限制T的类型,无论animals是类还是接口都是用extends
class Template<T extends Animals> {
    private T a;
    public Template(T a) {
        this.a = a;
    }
    public T get() {
        return this.a;
    }
}
ArrayList

ArrayList是 List接口 的 可变 数组 的 实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。

        List<String> al = new ArrayList<String>();
        al.add("a");
        al.add("b");
        al.add("c");
        al.add("d");
        al.add("e");
        al.set(0, "aa");
        Iterator<String> it = al.iterator();
        while (it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        for (String s : al) {
            System.out.println(s);
        }

        System.out.println(al.indexOf("aa"));
        System.out.println(al.remove(al.size() - 1));
        System.out.println(al.remove("b"));
        System.out.println(al.contains("aa"));
        System.out.println(al.get(0));
        System.out.println(al.isEmpty());
        al.clear();
    }
LinkedList

LinkedList是一个双向链表。它也可以被当作堆栈、队列或双端队列进行操作。LinkedList随机访问效率低,但随机插入、随机删除效率低。

        LinkedList<String> l = new LinkedList<String>();
        l.add("a");
        l.addFirst("b");
        l.addLast("c");
        Iterator<String> it = l.iterator();
        while(it.hasNext()) {
            String s = it.next();
            System.out.println(s);
        }
        for(String s : l) {
            System.out.println(s);
        }
        System.out.println(l.removeFirst());
        System.out.println(l.pollFirst());
使用LinkedList实现堆栈:
import java.util.Iterator;
import java.util.LinkedList;

public class LinkedListDemo2 {

    public static void main(String[] args) {
        MyStack<String> s = new MyStack<String>();
        s.push("a");
        s.push("b");
        s.push("c");
        s.pop();
        Iterator<String> it = s.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }
        MyQueue<String> q = new MyQueue<String>();
        q.push("a");
        q.push("b");
        q.push("c");
        q.pop();
        it = q.iterator();
        while(it.hasNext()) {
            System.out.println(it.next());
        }

    }
}


class MyStack<T> {
    private LinkedList<T> data;
    public MyStack() {
        data = new LinkedList<T>();
    }
    public void push(T e) {
        data.addFirst(e);
    }
    public T pop() {
        return data.removeFirst();
    }
    public Iterator<T> iterator() {
        return data.iterator();
    }
}
class MyQueue<T> {
    private LinkedList<T> data;
    public MyQueue() {
        data = new LinkedList<T>();
    }
    public void push(T e) {
        data.addLast(e);
    }
    public T pop() {
        return data.removeFirst();
    }
    public Iterator<T> iterator() {
        return data.iterator();
    }
}
HashMap

基于哈希表的 Map 接口的实现,以key-value的形式存在。在HashMap中,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置,我们总是可以通过key快速地存、取value。

        Map<String, String> map = new HashMap<String, String>();
        map.put("a", "aaa");
        map.put("b","bbb");
        map.put("c", "ccc");
        map.put("a", "bbb");
        System.out.println(map);

        Set<String> set = map.keySet();
        System.out.println(set);

        Collection<String> c = map.values();
        System.out.println(c);

        for (String string : set) {
            System.out.println(string +":" + map.get(string));
        }
        System.out.println(map.size());

        Set<Entry<String, String>> entry  =  map.entrySet();
        for(Entry<String,String>e:entry) {
            System.out.println(e.getKey() + ":" + e.getValue());
        }

HashMap与Hashtable的比较:
执行效率不同:HashMap是非线程安全的,是Hashtable的轻量级实现,效率较高;Hashtable是线程安全的,效率较低。
put方法对key和value的要求不同:
HashMap允许Entry的key或value为null;Hashtable不允许Entry的key或value为null,否则出现NullPointerException
有无contains方法:
HashMap没有contains方法;Hashtable有contains方法
当HashMap中存储自定义的对象时,可以重写 hashCode()和equals(Object obj)方法来修改判断对象是否相等的条件:

import java.util.HashMap;
import java.util.Map;

public class HashMapDemo2 {

    public static void main(String[] args) {
        Map<Student, String> m = new HashMap<Student,String>();
        m.put(new Student("a", 1), "a");
        m.put(new Student("a", 1), "a");
        m.put(new Student("b", 2), "a");
        m.put(new Student("c", 3), "a");
        System.out.println(m);
    }

}

class Student {
    private String name;
    private int age;
    public Student(String n, int a) {
        name = n;
        age    = a;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}
TreeMap

TreeMap类不仅实现了Map接口,还实现了java.util.SortMap接口,因此集合中的映射关系具有一定的顺序.但是在添加,删除,和定位映射关系上,TreeMap类比HashMap类的性能差一些.TreeMap类实现的Map集合中的映射关系是根据键值对象按一定的顺序排列的.因此不允许键对象是null.

import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;

public class TreeMapDemo1 {

    public static void main(String[] args) {
        TreeMap<String, String> t = new TreeMap<String, String>();
        t.put("a", "aaa");
        t.put("c", "bbb");
        t.put("b", "ccc");
        t.put("a", "ddd");
        System.out.println(t);

        Set<Entry<String, String>> s = t.entrySet();
        for(Entry<String, String>e : s) {
            System.out.println(e.getKey() + ":" + e.getValue());
        }
    }

}

当使用TreeMap存储我们自定义的对象时,对象必须是可排序的,有以下两种方法:

import java.util.Comparator;
import java.util.TreeMap;

public class TreeMapDemo2 {
    public static <T> void main(String[] args) {
        /**
         * treemap的key要可以排序,
         * 可以在类中实现Comparable接口
         * 或者在构造时new Compator匿名内部类
         */
        TreeMap<Person,  String> tm = new TreeMap<Person, String>();
        tm.put(new Person("zhangsan",39), "a");
        tm.put(new Person("lise",36), "b");
        tm.put(new Person("zhangsan",45), "c");
        System.out.println(tm);     


        TreeMap<Person, String> tm2 = new TreeMap<Person, String>(new Comparator<Person>() {

            @Override
            public int compare(Person o1, Person o2) {

                return o1.getName().compareTo(o2.getName());
            }

        });
        tm2.put(new Person("zhangsan",39), "a");
        tm2.put(new Person("lise",36), "b");
        tm2.put(new Person("zhangsa",39), "c");
        System.out.println(tm2);    
    }
}
class Person implements Comparable<Person> {
    private String name;
    private int age;
    public Person(String n, int a) {
        name = n;
        age    = a;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int compareTo(Person o) {
        if (this.age > o.age) {
            return 1;
        } else if(this.age < o.age) {
            return -1;
        }
        return 0;
    }

}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值