Java basis

基础知识

随机数

  1. Random使用,bound即上界,seed即随机数种子,代表时间,多次执行程序,Random产生的序列始终是一个序列。(网上又说以47为随机数种子产生随机数的随即率最大,对这句话不是很理解)
1
2
3
4
Random rand= new Random(47);//seed: 47
int []num=new int[30]
for(int i=0;i<25;i++)
num[i]=rand.nextInt(100);//bound: 100
  1. Math.random(),该方法是产生0和1之间(包括0,单不包括1)的一个随机double值。

  2. 生成一个大随机数,下面例子BigInteger有256位

1
BigInteger A = new BigInteger(256, new Random());

循环

Foreach

 这是一种从Java SE5开始的新的更简洁的for语法用于数组与容器,表示不必创建int变量去对由访问项构成的序列进行计数,foreach将自动产生每一项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Random rand=new Random(47);
float f[]=new float[10];
for(int i=0;i<10;i++)
f[i]=rand.nextFloat();
for(float x : f)
System.out.print(x);
for(char c : "An African Swallow".toCharArray())
System.out.print(c+" ");
for(int i : range(10))//0..9
printnb(c+" ");
print();
for(int i : range(5,10))//5..9,range(5,20,3),5..20 step 3
printnb(c+" ");
print();

文件流

序列化与反序列化

什么是序列化与反序列化

 对象序列化的定义是,将对象转态转化为字节流的过程,可以将其保存到磁盘文件或通过网络发送到任何其他程序,从字节流创建对象的相反的过程称为反序列化,并且创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。(这里可能是这个意思,比如在Java上将数据序列化存入文件中,而用C#又可以将文件中字节流反序列化为原来的数据)

 简单的说,把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。

具体体现

 首先需要使用Serializable关键字实现接口;注意对于存在类的继承关系的接口实现,只需实现父类的接口即可;

  1. 序列化:创建对象输出流,包装一个某种类型的目标输入流,如文件输入流,然后通过对象输入流的writeObject()方法写对象。
  2. 反序列化:创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流,然后通过对象输入流的readObject()方法读取对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person implements Serializable {//父类序列化
String gender,address,tel,id;
transient int age;
public Person(){}
//...方法实现省略
}
class Teacher extends Person{
public Teacher(){}
}
class Student extends Person{
public Student(){}
}
class Assistant extends Person{
public Assistant(){}
}

 序列化并以覆盖形式写入。

1
2
3
4
5
6
7
8
9
10
11
12
public static void AssistantInformationInput(){
try{
Person person=new Person();
File file=new File("D://t1.txt");
FileOutputStream fos=new FileOutputStream(file);
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(person);
oos.close();
}catch(Exception e){
e.printStackTrace();
}
}

 序列化并以追加形式多次写入。对象序列化不能向普通文件一样直接追加对象。

 在new ObjectOutputStream对象的时候,会执行它的构造方法,在构造方法中有一个writeStreamHeader();语句会被执行,这个方法的功能就是在文件中写入文件头。如果你写的方法在一个文件中重复new了ObjectOutputStream对象,则每次执行构造函数的时候都会执行这个方法写上一个文件头。Java默认的对象序列化是每次写入对象都会写入一点头aced 0005(占4个字节),然后每次读取都读完头然后在读内容。解决方法就是先判断文件是否存在。如果不存在,就先创建文件。然后写了第一个对象,也写入了头aced 0005。追加的情况就是当判断文件存在时,把那个4个字节的头aced 0005截取掉,然后在把对象写入到文件。这样就实现了对象序列化的追加。(需要注意的是,并非是每次序列化追加写入都需要减掉4字节,而是每次创建对象流的时候需要减)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void AssistantInformationInput(){
try{
long pos=0;boolean flag=false;
Scanner input=new Scanner(System.in);
File file=new File("D://t1.txt");
if(file.length()>0)
flag=true;
FileOutputStream fos=new FileOutputStream(file,true);
ObjectOutputStream oos=new ObjectOutputStream(fos);
if(flag) {
//此处是为了进行序列化内容的追加,而非覆盖
pos = fos.getChannel().position() - 4;
fos.getChannel().truncate(pos);
}
for(int i=1;i<=10;i++){
Person person=new Person();
person.setId(i);
oos.writeObject(person);
}
oos.close();
}catch(Exception e){
e.printStackTrace();
}
}
transient关键字

transient关键字标识的变量意味着不会被序列化。

输入流的几种常见形式
1
2
3
4
5
6
7
public class Main {
public static void main(String []args){
Scanner input_1=new Scanner(System.in);
Scanner input_2=new Scanner(new BufferedReader(new InputStreamReader(System.in)));
BufferedReader input_3=new BufferedReader(new InputStreamReader(System.in));
}
}

文件读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.io.*;

public class Main{
public static void main(String args[]){
try{
/* 读取 */
String path="D:\\1.txt";
File file=new File(path);
InputStreamReader isr=new InputStreamReader(new FileInputStream(file));
BufferedReader br=new BufferedReader(isr);
String line="";
while((line=br.readLine())!=null){
System.out.println(line);
}
/* 写入*/
File f=new File("D:\\2.txt");
if(!f.exists()){
f.createNewFile();
}
BufferedWriter bw=new BufferedWriter(new FileWriter(f));
bw.write("将这句话写入文件\r\n");// \r\n为换行
bw.flush();
bw.close();
}catch(Exception e){
e.printStackTrace();
}
}
}

泛型与集合

集合

迭代器

 简单的说迭代器是用来帮助访问集合中的每个元素。所有的集合类(Set、Sequence、Queue,除了Map)都实现了迭代接口Iterable,Iterable接口只有一个方法。

1
Iterator<T>iterator();

 它返回一个在一组T类型元素上的迭代器,Iterator是一个接口类型,它的方法如下。

方法名称 作用
hasNext() 如果仍有元素可以迭代,则返回true
E next() 返回迭代的下一个元素
remove() 迭代器指向的集合中移除迭代器返回的最后一个元素(可选操作)

 下面给出利用迭代器访问元素的实现

1
2
3
4
5
Iterator<Acount>iterator=accounts.iterator();
Account account=null;
while(iterator.hasNext()){
account=iterator.next();
}

 集合实例名为accounts,包含的都是Accout类型的对象。

List

  1. list是有序,可以通过整数索引(从0开始)访问列表中的元素。
  2. 列表通常允许重复的元素。
  3. list接口提供了特殊的迭代器,称为ListIterator,除了允许Iterator接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。
  4. 某些实现List接口的列表类,对是否允许null出现在列表中有着不同的规定。
ArrayList

 ArrayList是基于数组的,在初始化ArrayList时,会构建空数组,ArrayList是无序的,是按添加顺序的进行排列,但是他有sort方法。

实现
1
2
List<Integer>list=new ArrayList<>();
ArrayList<Integer>arraylist=new ArrayList<>();

 对于赋值右段的"<>",编译器可以自动推断出其应具备的类型。

字符串

字符串常量与字符串变量

  首先java编译器对于字符串常量会创建一个对象;另外,字符串中所有字符都是Unicode字符,所以每个字符占两个字节;

  在java中,字符串常量在内存中由编译器分配固定区域,而字符串变量只是一个引用,所以不能通过字符串变量对常量进行修改,即想要通过引用修改字符串是不可行的;

1
2
String str = "hello world!";  
str = "hello java";

  如上面的操作只是修改了str的引用,并非修改了其值;

1
2
String str = "hello world!";  
String str1 = "hello" + " world!";

  而上面的操作,其实str和str1都是对同一个字符串常量的引用,在str1进行赋值时会自动在字符串常量池中检测是否有该串,若有就直接利用它,这样做的优点是减少了程序中需要存储的字符串的数量和空间;

字符串的比较

  java里的字符串的比较有两种;str1.equals(str2),即判断str1的字符序列是否等于str2的字符序列,==,判断两个对象是否引用同一个对象;另外还有str1.compareTo(str2),该方法用来判断str1、str2两个字符串的字典序大小关系;看下面代码即可;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Main {  
public static void main(String[] args) {
Scanner input =new Scanner(new BufferedInputStream(System.in));
String str = "hello world!";
String str1 = "hello" + " world!";
String str2 = new String("hello world!");
String str3 = new String("hello world!");
System.out.println(str == str1); // true
System.out.println(str.equals(str1)); // true
System.out.println(str == str2); // false
System.out.println(str.equals(str2)); // true
System.out.println(str2 == str3); // false
System.out.println(str2.equals(str3)); // true
}
}
/*
true
true
false
true
false
true
*/

  注意new的使用,比如String s = new String("hello");则是声明了一个对String的引用,然后分配一个内存空间,之后调用构造函数初始化该空间的值为"hello",然后将该地址的值赋给已创建的String的引用;String s= "hello",则是先去字符串常量池中寻找受否存在"hello",若有则直接使用它,使得引用s指向该字符串常量;(这一段是个人理解)

其他

  String是一个不可变的对象,每次对于String进行操作或者使用其相关的一些函数,都可能会生成一个新的String的对象,所以如果需要经常对其进行操作,那么会反复生成大量的临时对象,这是非常浪费时间的;

内存

堆、栈、常量池等

  1. 常量池:未经new的常量
  2. 堆区:成员变量的引用,new出来的变量;成员变量的引用在堆区,是因为成员变量所属的对象在堆区,所以它也在堆区;
  3. 栈区:局部变量的引用;局部变量的引用在堆区,是因为局部变量不属于某一个对象,在调用时才被加载,所以在栈区;

细节

成员变量与方法

  1. Java的main方法一样要写在public修饰的类里, >main方法是JVM(java虚拟机)自动调用 > >JVM调用main方法的位置自然不会在某个类中、或某个包中,因此只有当main方法在公有级别上时,才对JVM可见,所以mian方法需要public修饰, > main方法所在的类也需要public修饰符。 > 另外由于main方法是所有程序的入口,也就是main被调用时没有任何对象创建,不通过对象调用某一方法,只有将该方法定义为静态方法,所以main方法是一个静态方法,既需要static修饰。 > JVM对于java程序已经是最底层,由它调用的方法的返回值已经没有任何地方可去,因此,main方法返回值为空,既需用void修饰。