本篇笔记主要记录了JavaSE的学习。
- JavaSE(Java Platform Standard Edition即Java平台标准版)
- JavaEE(Java Platform Enterprise Edition即Java平台企业版)
- JavaME(Java Platform Micro Edition即Java平台微型版)
本篇笔记主要记述了JavaSE;
关于Java这门编程语言,在初次接触时变听闻其具有“一次编写,到处运行”的特点,之前学习iOS开发时、必备Mac OS系统或是黑苹果。
Java这个优势得益于JVM(Java虚拟机)的存在。当我们完成了编写源程序(.java文件)之后,编译程序可以得到字节码文件(.class)。不同的平台有不同的JVM,JVM将字节码文件翻译成平台能识别的机器语言。
学过C语言的同学都知道,需要用指针指向堆中的变量。对于Objective-C就更厉害了,所有对象皆为指针(所有的对象皆被指针变量所引用)。而在Java中没有指针的概念、只有引用的概念,所有的参数赋值皆为值传递(基本类型值传递和引用值传递都是一个值拷贝的过程,所以皆称为值传递)。而在C++的说法中既有传值调用(基本类型值传递)又有传址调用,而传址调用又有引用和指针的概念。
//这是一个保存了基本类型变量的数组
1、int[] 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 };
一般我们都会以上述方式声明一个一维数组.需要注意的是:
先看Java的定义方式:
1、int[][] b = {{1,2},{3,4}};
2、int[][] a = new int[3][];
第二种数组创建的方式可能让人感到很诧异,因为我们在学习C语言的二维数组时清楚的记得是:行数可以省略,列数不可以省略。而在Java中却是列数可以省略,行数不可以省略。
我们先来对比一下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中提供了三种字符串类:String、StringBuilder和StringBuffer:
String对象是不可变的即不可被修改:
String s1 = "aaa";
s1 = "bbb";
第一行代码声明了一个字符串的引用变量s1,和一个字符串对象“bbb”,第二行看似修改了字符串对象,只是s1引用了不同的字符串对象而已,“aaa”还是那个“aaa”并没有被修改。
String对象有两种创建方式
1、 String s1 = "aaa";
2、 String s2 = "aaa";
3、 String s3 = new String("aaa");
4、 String s4 = new String("aaa");
字符串字面值存储于字符串池中,且不会存在重复的字符串实例,这也是我们优先使用的一种方式。使用new生成的字符串对象存在于堆中,如图示。
为什么将String设计为不可变?
只读的特性是为了提高效率。如果对字符串修改的效率很高,就需要使用另外两个类了。
StringBuffer对象是线程安全的可变字符序列,StringBuilder是单线程使用的可变字符序列,除线程问题外二者等价。
通常优先使用StringBuilder类,因为它不需要执行同步操作,所以效率更高。
Java中的枚举是非常强大的,事实上其定义的类型就是一个类
enum Color {
// 枚举类型的值必须作为第一条语句出现
RED,
BLUE,
YELLOW;
private Color() {
System.out.println("construter");
}
}
枚举值都是public static final类型的常量
枚举的构造器必须是私有的,不允许定义为public,构造器在第一次使用枚举值的时候被调用,有几个枚举值就调用多少次
Java中,switch表达式的case语句也是非常强大的,在Java5前只允许case的类型有:byte、short、int、char(long、double都不可以,因为可能会造成精度丢失),Java5中加入了枚举,Java7中加入了string
标准Java库是由一系列包组成,比如程序默认为我们引入的java.lang包,还有工具包java.util等等等。通过打包可以有效的防止命名冲突,同时也就有了默认访问控制权限。
我们使用package声明一个包,使用import导入一个包(或者在类前面加上完整的包名)。为了使代码具有层次性,我们开发中会编写很多包,包名必须具有唯一性,且由小写字母组成,一般使用域名的倒置作为包的唯一前缀,如:com.afei.demo。
一个类可以使用同一个包中的所有类,和其它包中的公开类。而在一个包中,只有一个公开类,而且该类名与文件名一致。
public Person() {
this("afei", "man", 0.00);
}
public Person(String name, String sex, double money) {
this.name = name;
this.sex = sex;
this.money = money;
}
方法重载:同一个类中,方法名相同、参数列表不同的方法同时存在称为方法重载。在Objective-C中不允许方法重载,因为它要求方法名是唯一的。而在C++和Java中解析方法的规则是方法签名(方法名+参数列表),而不是方法名。所以可以进行方法重载,且C++和Java的方法重载的要求是一致的。
方法重载要求方法名一致,参数列表不一致(参数个数不一致或参数类型不一致或二者都不一致),仅有返回类型不同的方法不能称为重载(方法签名相同就无法决定所要调用的方法)。
继承(extends):所谓继承就是基于已存在的类来构建新类,通过继承实现复用。子类还可以为自己添加新的属性和方法来完善自己。C++是允许多继承的,而Java和Objective-C是单继承的语言。所谓单继承指的是:每个类只允许直接继承一个类。
继承体现的是is-a关系。
我们所创建的类若不显式继承别的类,则默认继承Object类。
instanceof运算符是用来判断:一个对象是否是特定类或者其子类的一个实例,若是则返回true,否则返回false。
对象进行向下转型之前(下面多态是介绍),可以使用该运算符进行判断以避免抛出异常。
方法重写:子类中可以对父类的方法进行改写。重写的方法和父类的方法名称、参数列表、返回值必须完全一致。
重写方法的访问权限可以和父类被重写方法的权限不一致,但是不能使用比父类被重写方法更严格的访问权限
父类中的私有方法不能被重写
如果只是对父类的方法进行扩充,可以使用super.函数名调用父类的方法。
抽象:对于一个事物,将我们所关注的内容提取出来。
抽象方法:只有方法头没有方法体的方法称为抽象方法;抽象方法使用abstract来修饰。
抽象类:使用abstract修饰类称为抽象类。抽象类不能被实例化。抽象类中可以有具体方法,可以没有抽象方法
一个类若继承了一个抽象类,要么实现抽象类中的抽象方法,要么继续作为一个抽象类。
接口定义了不相关的类之间的共同特征,接口不关注类之间的关系、只是抽取这些类所共同具备的属性和行为。
而抽象类与实现类则是一种被继承关系。
一个类可以继承一个类,并且同时实现多个接口(单继承,多实现).
接口与接口之间可以使用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("由接口直接调用");
}
}
多态:同类对象接收相同消息产生不同的行为。
多态产生的三个必要条件:
- 需要存在继承
- 需要有方法重写
- 向上转型:父类或接口的引用变量引用子类对象
多态的作用:
- 将接口和实现分离
- 消除类型之间的耦合
- 提高程序的扩展性和可维护
向上转型是安全的,不需要强制类型转换,但在转型之后,只能调用父类或接口中存在的方法,这就可能造成子类自己扩展的方法丢失。
与向上转型相反的是向下转型,这是不安全的,需要进行强制类型转换(同时最好使用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();
我们在编写程序时,有些对象将不再被任何变量引用,如作用域使引用变量废弃或人为将对象置为null,如此我们将再也访问不到堆中的对象,若堆中的对象不被及时处理将造成堆内存告急。在Java运行时系统中有一个线程负责清除不再被使用的对象即垃圾回收器。
垃圾回收器会定期扫描内存,对于被使用的对象加上标记,未加标记的对象则会被清除。
内部类:一个类可以定义在另一个类的内部,该类称为内部类。Java中提供了四种内部类:成员内部类、静态内部类、匿名内部类和局部内部类。
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成员变量
成员内部类可以直接访问外部类的成员包括私有成员,外部类不能直接访问内部类的成员,只有先建立内部类的对象才可以访问。
成员内部类的出现可以间接的实现多继承,如下代码:
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语句的一部分来声明并创建它们的实例。
匿名内部类必须继承一个类,或者实现一个接口。
匿名内部类中不能定义静态代码块。
这里只是给出类匿名内部类的概念,具体的使用会在后面给出。
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();
}
}
二、实现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();
}
}
三、实现Callable接口:
package ThreadDemo3;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable {
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 task1 = new FutureTask(mt1);
FutureTask task2 = new FutureTask(mt2);
new Thread(task1).start();
new Thread(task2).start();
System.out.println(task1.get());
System.out.println(task2.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] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 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]是不是出现了3到10次
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 = "[email protected]";
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类是反射操作的源头,所有的反射都要从此类开始进行
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 {
void show();
}
//泛型类
class Template {
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 t = new Template(c);
t.get().eat();
// Template
// 无限定通配符
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是 List接口 的 可变 数组 的 实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。ArrayList 是一个数组队列,相当于动态数组。它由数组实现,随机访问效率高,随机插入、随机删除效率低。
List al = new ArrayList();
al.add("a");
al.add("b");
al.add("c");
al.add("d");
al.add("e");
al.set(0, "aa");
Iterator 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 l = new LinkedList();
l.add("a");
l.addFirst("b");
l.addLast("c");
Iterator 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());
import java.util.Iterator;
import java.util.LinkedList;
public class LinkedListDemo2 {
public static void main(String[] args) {
MyStack s = new MyStack();
s.push("a");
s.push("b");
s.push("c");
s.pop();
Iterator it = s.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
MyQueue q = new MyQueue();
q.push("a");
q.push("b");
q.push("c");
q.pop();
it = q.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
class MyStack {
private LinkedList data;
public MyStack() {
data = new LinkedList();
}
public void push(T e) {
data.addFirst(e);
}
public T pop() {
return data.removeFirst();
}
public Iterator iterator() {
return data.iterator();
}
}
class MyQueue {
private LinkedList data;
public MyQueue() {
data = new LinkedList();
}
public void push(T e) {
data.addLast(e);
}
public T pop() {
return data.removeFirst();
}
public Iterator iterator() {
return data.iterator();
}
}
基于哈希表的 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 m = new HashMap();
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类不仅实现了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 t = new TreeMap();
t.put("a", "aaa");
t.put("c", "bbb");
t.put("b", "ccc");
t.put("a", "ddd");
System.out.println(t);
Set> s = t.entrySet();
for(Entrye : s) {
System.out.println(e.getKey() + ":" + e.getValue());
}
}
}
当使用TreeMap存储我们自定义的对象时,对象必须是可排序的,有以下两种方法:
import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapDemo2 {
public static void main(String[] args) {
/**
* treemap的key要可以排序,
* 可以在类中实现Comparable接口
* 或者在构造时new Compator匿名内部类
*/
TreeMap tm = new TreeMap();
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 tm2 = new TreeMap(new Comparator() {
@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 {
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;
}
}