1.数据结构
-
枚举(Enumeration)
枚举接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。
枚举接口定义了一种从数据结构中取回连续元素的方式。
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。
现已比较少使用,一般会出现在Vector和Properties这些传统类所定义的方法中
import java.util.Vector;
import java.util.Enumeration;
public class CodeExercise {
public static void main(String[] args) {
Enumeration<String> days;
Vector<String> dayNames = new Vector<String>();
dayNames.add("Sunday");
dayNames.add("Monday");
dayNames.add("Tuesday");
dayNames.add("Wednesday");
dayNames.add("Thursday");
dayNames.add("Friday");
dayNames.add("Saturday");
//将Vector元素给到枚举
days = dayNames.elements();
// 测试枚举是否包含更多的元素
while(days.hasMoreElements()){
// 返回下一个元素
System.out.println(days.nextElement());
}
}
}
-
位集合(BitSet)
一个Bitset类创建一种特殊类型的数组来保存位值。
public class CodeExercise {
public static void main(String[] args) {
BitSet bits1 = new BitSet(16);
BitSet bits2 = new BitSet(16);
for(int i=0;i<16;i++){
if((i%2) == 0) bits1.set(i);
if((i%2) != 0) bits2.set(i);
}
// bits1:{0, 2, 4, 6, 8, 10, 12, 14}
System.out.println("bits1:"+bits1);
// bits2:{1, 3, 5, 7, 9, 11, 13, 15}
System.out.println("bits2:"+bits2);
// 取交
bits2.and(bits1);
System.out.println("bits2 and bits1:"+bits2);
// 取并
bits2.or(bits1);
System.out.println("bits2 or bits1:"+bits2);
// 取异或
bits2.xor(bits1);
System.out.println("bits2 xor bits1:"+bits2);
}
}
-
向量(Vector)
Vector类实现了一个动态数组。和ArrayList很相似,但两者是不同的:- Vector是同步访问的
- Vector包含了许多传统的方法,这些方法不属于集合框架
Vector主要用在事先不知道数组大小,或者只是需要一个可以改变大小的数组的情况
import java.util.Vector;
import java.util.Enumeration;
public class CodeExercise {
public static void main(String[] args) {
// capacity只会以2位单位增加
Vector v = new Vector(3,2);
// size = 0
System.out.println("the initial size of v:" + v.size());
// capacity = 3
System.out.println("the initial capacity of v:" + v.capacity());
// 将指定的组件添加到此向量的末尾,将其大小增加 1。
v.addElement(new Integer(3));
v.addElement("string");
v.addElement(new Double(5.0));
v.addElement(new Float(6.6));
// size 4
System.out.println("the size of v:" + v.size());
// capacity 5
System.out.println("the capacity of v:" + v.capacity());
Enumeration vEnum = v.elements();
System.out.println("elements in vector");
while(vEnum.hasMoreElements()){
System.out.print(vEnum.nextElement()+" ");
}
}
}
-
栈(Stack)
栈是Vector的一个子类,栈内元素后进先出。
import java.util.*;
public class CodeExercise {
public static void main(String[] args) {
Stack<Integer> st = new Stack<Integer>();
// stack.empty() 判断栈是否为空
while(st.empty() == true){
System.out.println("st is empty.");
break;
}
st.push(1);
st.push(2);
st.push(3);
st.push(4);
st.push(5);
System.out.println(st);
// a = 5
int a = st.pop();
System.out.println("Pop element is: " + a);
}
}
-
字典(Dictionary)
Dictionary是一个抽象类,用来存储key/value对,作用和Map类相似。
给出key和value的值,就可以将value存储在Dictionary的对象中。一旦被存储,就可以通过key来获取它。
Dictionary类已经过时,实际开发中,往往通过实现Map接口来获取Key/Value的存储功能。
Map代码:
import java.util.*;
public class CodeExercise {
public static void main(String[] args) {
Map m1 = new HashMap();
m1.put("Zara",8);
m1.put("Mahnaz","31");
m1.put("Ayan","12");
m1.put("Daisy","12");
System.out.println(m1);
}
}
-
哈希表(Hashtable)
Hashtable和HashMap类很相似,但它支持同步。注:Hashtable已经被淘汰了,简单来说就是,如果不需要线程安全,那么使用HashMap,如果需要线程安全,那么使用ConcurrentHashMap。
-
属性(Properties)
Properties继承于Hashtable。表示一个持久的属性集。
属性列表中每个键及其对应值都是一个字符串。
import java.util.*;
public class CodeExercise {
public static void main(String[] args) {
Properties capitals = new Properties();
Set states;
String str;
capitals.put("Illinois","Springfield");
capitals.put("Missouri","Jefferson City");
capitals.put("Washington","Olympia");
capitals.put("California","Sacramento");
capitals.put("Indiana","Indianapolis");
states = capitals.keySet();
// 迭代器一种用于访问集合的方法
Iterator itr = states.iterator();
while(itr.hasNext()){
str = (String)itr.next();
System.out.println("The capital of " + str + " is " + capitals.getProperty(str));
}
}
}
2.控制语句
Java控制语句大致可分为三大类:
- 选择语句
- if 语句
- if...else 语句
- if...else if...else 语句
- 嵌套的if...else 语句
- switch语句
default
在没有case
语句的值和变量值相等的时候执行。default
分支不需要break
语句。
- 循环语句
- while 循环
- do while 循环
- for 循环
- for each 循环
- 中断语句
- break关键字
- continue关键字
- return关键字,跳出函数整体
public class CodeExercise {
public static void main(String[] args) {
int[] numbers = { 10, 20, 30, 40, 50 };
for (int x : numbers) {
if (x == 30) {
// 直接跳出main函数
return;
}
System.out.print(x);
System.out.print("\n");
}
System.out.println("return 示例结束");
}
}
输出:
10
20
Process finished with exit code 0
实践参考
- 选择分支特别多的情况下,
switch
语句优于if...else if... else
语句 -
switch
语句尽量考虑default
,并且将其放在最后 -
for each
循环优先于传统的for
循环 - 不要循环遍历容器元素,然后删除特定元素。正确的做法是遍历容器的迭代器(
Iterator
),删除元素。
3.面向对象编程
面向对象编程的三大主要特征:
- 封装性
- 继承性
- 多态性
封装性:在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
封装最主要的功能在于我们能修改自己实现的代码,而不用修改那些调用我们代码的程序片段。
封装的简单实现:
- 修改属性的可见性(一般限制为private)
public class Person {
private String name;
private int age;
}
- 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问。
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
注:以上实例中,public方法是外部类访问该类成员变量的入口,这些方法被称为getter和setter方法。
为什么需要setter和getter?
继承性:
参考
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
子类可以直接访问父类中的非私有的属性和行为。通过extends
让类与类之间产生继承关系。
class Succlass extends Class{}
继承的特点
1.Java只支持单继承,不支持多继承
class Demo extends Demo1, Demo2... //error
2.Java支持多重继承(继承体系)
class B extends A { }
class C extends A { }
多态性:
对象在不同时刻表现出来的不同状态。换句话可以理解为,同一个事件发生在不同的对象上会产生不同的结果。
多态的前提:
- 继承
- 重写
- 父类引用指向子类对象:
Parent p = new Child();
多态的实现方式:
1.重写
2.接口
3.抽象类和抽象方法
Overloading 的条件 以及与Overriding的区别:
重写(Overriding)是子类对父类中允许访问的方法进行重新编写,返回值和形参都不能改变。
重写的好处在于,子类可以根据需要实现父类的方法。
重写方法不能跑出新的检查异常或者比被重写方法申明更加宽泛的异常。例如:父类的一个方法申明了一个检查异常IOException,但是在重写这个方法的时候不能抛出Exception异常,因为Exception是IOException的父类。
public class CodeExercise {
public static void main(String[] args) {
Test a = new Test();
ChildTest b = new ChildTest();
a.f(3);
b.f(4);
b.f1();
}
}
class Test{
public void f(int a){
System.out.println("Parent class " + a);
}
}
class ChildTest extends Test{
public void f(int b){
// 子类调用父类的被重写的方法时,用super关键字
super.f(b);
System.out.println("Child class " + b);
}
public void f1(){
System.out.println("Child class f1");
}
}
output:
Parent class 3
Parent class 4
Child class 4
Child class f1
重载(Overloading)是在一个类里面,方法名字相同,而参数不同,返回类型不限制。
每个重载的方法都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
public class CodeExercise {
public void test(){
System.out.println(1);
}
public int test(int a){
return a;
}
public String test(String a){
return a;
}
public String test(String a,String b){
return a+b;
}
public static void main(String[] args) {
CodeExercise oj = new CodeExercise();
oj.test();
System.out.println(oj.test(2));
System.out.println(oj.test("test"));
System.out.println(oj.test("test","test1"));
}
}
output:
1
2
test
testtest1
总结:
方法的重写和重载时Java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
- 重载时一个类中定义了多个方法名相同,但参数数量不同或类型不同或次序不同。
- 重写是在子类方法与父类方法的名字相同,并且参数个数与类型一样,唯一不同的则是实现的内核。
4.数组、对象和类
数组:数组是用于存储固定大小的同类型元素。
对象:在java中,对象主要包括属性和方法。
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和方法。
抽象类:如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。由于抽象类不能实例化对象,所以抽象类必须被继承
5.接口、继承关系、多态、内部类
接口:接口是抽象方法和常量值的集合。从本质上讲,接口是一种特殊的抽象类,这种抽象类只包含常量和方法的定义,而没有变量和方法的实现。
一个类通过继承接口的方式,从而继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但他们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
接口的声明:
public interface Animal {
public void eat();
public void travel();
}
接口的实现:
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
接口的继承:
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,故实现Hockey接口的类需要实现6个方法。
同理,实现Football接口的类需要实现5个方法。
抽象类:由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。通常在设计阶段决定要不要设计抽象类。
例如一个图形类Shape
,它有一个方法draw()
,Shape
其实是一个抽象的概念,具体的draw()
方法并不知道如何去实现,只有子类才知道如何实现的方法,这种方法一般被定义为抽象方法。
一个类在继承抽象类之后,必须实现抽象类中定义的所有抽象方法,除非它自己也声明为抽象类。
public abstract class Shape{
// .... 其他代码
public abstract void draw();
}
public class Circle extends Shape{
// ...其他代码
@Override
public void draw(){
}
}
为什么需要抽象类?
引入抽象类和抽象方法,是Java提供的一种语法工具。
使用抽象方法,而非空的方法体,子类就知道必须要实现该方法,而不可能忽略。
使用抽象类,类的使用者创建对象的时候,就知道他必须要使用某个具体的子类,而不可能无用不完整的父类。
抽象类和接口
- 抽象类中的方法可以有实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以使各种类型,接口中的成员变量只能是
public static final
类型的。 - 接口中不能含有静态代码块以及静态方法,而抽象类可以有。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口
内部类:
将一个类定义在另一个类里面,里面的那个类就被称为内部类。
为什么要使用内部类
是因为,每个内部类都能独立地继承一个接口的实现,所以无论外部类是否已经继承了某个接口的实现,对于内部类都没有影响。
内部类比较好的解决了多重继承的问题。
附:Java的多重继承问题:
多重继承主要指的是一个类可以同时继承多个类。例如A类继承自B类和C类。
若B,C继承A类,即A -> B,A -> C,并且D继承B,C。若B,C都改写了A中的一个方法void a()
成void a1()
和void a2()
,D类不知道该用a1还是a2。
但在接口中并不会出现这样的问题是因为,接口是抽象类,它只规定了a方法并没有实现,所以不会出现a方法有两种实现版本的问题。
内部类的主要分类:
成员内部类、局部内部类、匿名内部类和静态内部类。
- 成员内部类
是最普通的内部类,他是外部类的一个成员,所以他是可以无限制访问外部类所有成员属性和方法,但是外部类则需要通过内部类实例来访问内部类的成员属性和方法。
public class CodeExercise {
private String sex = "Male";
public static String name = "OutterMatthew";
//外部类的方法
public void outterdisplay(){
System.out.println("OutterClass" );
}
//推荐使用 getter来获取成员内部类,尤其是该内部类的构造参数无参数时
public InnerClass getterInner(){
return new InnerClass();
}
//成员内部类
public class InnerClass{
public void display(){
// 非静态内部类中可以调用外部类的任何成员,无论静态还是非静态
System.out.println("InnerClass call Outter name: " + name);
System.out.println("InnerClass call Outter sex: " + sex);
outterdisplay();
}
}
public static void main(String[] args) {
// new CodeExercise().getterInner()是通过外部类的object调用getterInner,从而获取成员内部类
CodeExercise.InnerClass inner = new CodeExercise().getterInner();
inner.display();
}
}
- 局部内部类
局部内部类是嵌套在方法和作用于内的。
局部内部类即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。
注意:局部内部类中不可定义static变量,可以访问外部类的局部变量(即方法内的变量),但变量必须是final
的。
public class CodeExercise {
private int a = 1;
private int out_n = 2;
//外部类的方法
public void outterdisplay(final int k){
final int a = 100;
final int b = 200;
// 局部内部类
class InnerClass{
int a = 101;
// static int m = 10; 在非静态内部类中不能存在静态成员
InnerClass(){
display();
}
public void display(){
// 没有与外部类同名的变量,可直接调用
System.out.println("局部内部类访问外部类非同名实例变量: " + out_n);
// 调用局部变量(方法内的变量),必须final修饰!
System.out.println("局部内部类访问方法内的变量: " + k);
// 若内部类和外部类同名,则访问的是内部类的变量
System.out.println("变量同名,则访问内部类的: " + a);
// this.同名变量 访问的也是内部类变量
System.out.println("加 this.变量名,访问的也是内部类变量: " + this.a);
// 外部类类名.this.同名变量名 访问的是外部类变量
System.out.println("外部类类名.this.变量名,访问的是外部类变量: " + CodeExercise.this.a);
}
}
// 创建新的内部类对象
new InnerClass();
}
public static void main(String[] args) {
CodeExercise outer = new CodeExercise();
outer.outterdisplay(300);
}
}
匿名内部类
匿名内部类往往是没有名字的静态内部类
使用static修饰的内部类称之为静态内部类,也被称为嵌套内部类。
静态内部类和非静态内部类之间的最大区别:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外部类,但是静态内部类却没有。这就意味着:
- 静态内部类的创建时不需要依赖外部类的。
- 静态内部类不能使用任何外部类的非static成员变量和方法。
public class CodeExercise {
private String sex = "Male";
public static String name = "OutterMatthew";
//外部类的方法
public void display(){
// 外部类访问静态内部类
System.out.println("OutterClass call static InnerClass1"+ InnerClass1.name1);
// 静态内部类可以直接创建实例,不需要依赖外部类
new InnerClass1().display();
CodeExercise.InnerClass2 inner2 = new CodeExercise().new InnerClass2();
System.out.println("OutterClass call static InnerClass2" + inner2.name2);
inner2.display();
}
//静态内部类
public static class InnerClass1{
// 在静态内部类中可以存在静态成员
public static String name1 = "InnerMatthew1";
public void display(){
// 静态内部类只能访问外部类的静态成员变量和方法
// 不能访问外部类的非静态成员变量和方法
System.out.println("OutterClass1 name: " + name);
}
}
//非静态内部类
public class InnerClass2{
// 在非静态内部类中不能存在静态成员
public String name2 = "InnerMatthew2";
public void display(){
// 非静态内部类中可以调用外部类的任何成员,无论静态还是非静态
System.out.println("OutterClass2 name: " + name);
System.out.println("OutterClass2 sex: " + sex);
}
}
public static void main(String[] args) {
CodeExercise outer = new CodeExercise();
outer.display();
}
}