笔记较为简单,主要记录了一些Java与C#某些地方的相似与区别,适合简单浏览速通,不细致不深入。

基础

常量

1
final int a = 10; // final定义常量

数据类型

在Java中,整数类型包括以下几个:

类型大小范围
byte 字节型8个bit,也就是1个字节-128~+127
short 短整形16个bit,也就是2个字节-32768~+32767
int 整形32个bit,也就是4个字节-2147483648 ~ +2147483647
long 长整形(+L/l)64个bit,也就是8个字节9223372036854775808 ~ +9223372036854775807
float 单精度浮点型(+F/f)32个bit,也就是4个字节
double 双精度浮点型64个bit,也就是8个字节
char 字符型16个bit,也就是2字节,它不带符号0 ~ 65535
boolen 布尔类型true,false

隐式类型转换:小转大,大不转小

强转与C一样:(数据类型)变量

字符串支持“+”拼接

面向对象

重载

相同名称的方法可以定义多次,传入不同类型的参数和返回类型,在使用时会自动匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int sum(int a, int b)
{
return a + b;
}

public double sum(double a, double b)
{
return a + b;
}

public String sum(String a, String b)
{
return a + b;
}
1
2
XX.sum(1.5, 2.2); // 使用时会自动匹配方法
XX.sum(6, 9);

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
public class Person {
String name;
int age;
String sex;

Person(){ //构造方法不需要指定返回值,并且方法名称与类名相同
name = "小明"; //构造方法会在对象创建时执行,我们可以将各种需要初始化的操作都在这里进行处理
age = 18;
sex = "男";
}
// 如果不写构造方法就会有一个默认的什么也没有的构造方法
}

构造方法可以重构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person {
String name;
int age;
String sex;

Person(String name, int age, String sex){ //跟普通方法是一样的
this.name = name;
this.age = age;
this.sex = sex;
}

Person(){
name = "小明";
}
}

构造方法会在new的时候自动执行:

1
2
3
4
5
public static void main(String[] args) {
Person p = new Person(); //这里的new Person()其实就是在调用无参构造方法
Person h = new Person("小红", 15, "女"); // 这里使用有参的构造方法
System.out.println(p.name);
}

静态变量和静态方法

一旦被声明为静态,那么通过这个类创建的所有对象,操作的都是同一个目标,也就是说,对象再多,也只有这一个静态的变量或方法。一个对象改变了静态变量的值,那么其他的对象读取的就是被改变的值。在静态方法中,无法获取成员变量的值,但是静态方法可以访问到静态变量。

1
2
3
4
5
6
public class Person {
String name;
int age;
String sex;
static String info; //这里我们定义一个info静态变量
}

包 = namespace

packa,import

访问权限

当前类同一个包下的类不同包下的子类不同包下的类
public
protected
默认
private

继承:extends

(如果父类没有使用默认构造函数,子类需要调用super父类构造方法(super代表这个子类的父类))

1
2
3
4
5
6
7
8
9
public class Student extends Person{
public Student(String name, int age, String sex) { //因为学生职业已经确定,所以说学生直接填写就可以了
super(name, age, sex, "学生"); //使用super代表父类,父类的构造方法就是super()
}

public void study(){
System.out.println("我的名字是 "+name+",我在学习!");
}
}

判断一个对象是什么类:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
Person person = new Student("小明", 18, "男");
if(person instanceof Student) { //我们可以使用instanceof关键字来对类型进行判断
System.out.println("对象是 Student 类型的");
}
if(person instanceof Person) {
System.out.println("对象是 Person 类型的");
}
}

重写

注意,方法的重写不同于之前的方法重载,不要搞混了,方法的重载是为某个方法提供更多种类,而方法的重写是覆盖原有的方法实现,比如我们现在不希望使用Object类中提供的equals方法,那么我们就可以将其重写了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person{
...

@Override //重写方法可以添加 @Override 注解,有关注解我们会在最后一章进行介绍,这个注解默认情况下可以省略
public boolean equals(Object obj) { //重写方法要求与父类的定义完全一致
if(obj == null) return false; //如果传入的对象为null,那肯定不相等
if(obj instanceof Person) { //只有是当前类型的对象,才能进行比较,要是都不是这个类型还比什么
Person person = (Person) obj; //先转换为当前类型,接着我们对三个属性挨个进行比较
return this.name.equals(person.name) && //字符串内容的比较,不能使用==,必须使用equals方法
this.age == person.age && //基本类型的比较跟之前一样,直接==
this.sex.equals(person.sex);
}
return false;
}
}

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Person {   //通过添加abstract关键字,表示这个类是一个抽象类
protected String name; //大体内容其实普通类差不多
protected int age;
protected String sex;
protected String profession;

protected Person(String name, int age, String sex, String profession) {
this.name = name;
this.age = age;
this.sex = sex;
this.profession = profession;
}

public abstract void exam(); //抽象类中可以具有抽象方法,也就是说这个方法只有定义,没有方法体
}

抽象类的子类必须要实现抽象类中所有抽象方法,除非抽象类的子类也是抽象类。

抽象类无法直接实例化。

接口

interface定义接口

1
2
3
public interface Study {    //使用interface表示这是一个接口
void study(); //接口中只能定义访问权限为public抽象方法,其中public和abstract关键字可以省略
}

implements实现接口

1
2
3
4
5
6
7
8
9
10
public class Student extends Person implements Study {   //使用implements关键字来实现接口
public Student(String name, int age, String sex) {
super(name, age, sex, "学生");
}

@Override
public void study() { //实现接口时,同样需要将接口中所有的抽象方法全部实现
System.out.println("我会学习!");
}
}

接口可以多继承

接口可以设置默认实现:

1
2
3
4
5
6
7
public interface Study {
void study();

default void test() { //使用default关键字为接口中的方法添加默认实现
System.out.println("我是默认实现");
}
}

接口可以继承自其他接口

枚举

枚举类也可以添加成员方法

1
2
3
4
5
6
7
8
9
10
11
12
public enum Status {
RUNNING("睡觉"), STUDY("学习"), SLEEP("睡觉"); //无参构造方法被覆盖,创建枚举需要添加参数(本质就是调用的构造方法)

private final String name; //枚举的成员变量
Status(String name){ //覆盖原有构造方法(默认private,只能内部使用!)
this.name = name;
}

public String getName() { //获取封装的成员变量
return name;
}
}

包装类与特殊包装类

BigInteger , 超大整数类型,没有限制

1
2
3
4
public static void main(String[] args) {
BigInteger i = BigInteger.valueOf(Long.MAX_VALUE); //表示Long的最大值,轻轻松松
System.out.println(i);
}

BigDecimal , 更高精度浮点

1
2
3
4
5
6
7
public static void main(String[] args) {
BigDecimal i = BigDecimal.valueOf(10);
i = i.divide(BigDecimal.valueOf(3), 100, RoundingMode.CEILING);
//计算10/3的结果,精确到小数点后100位
//RoundingMode是舍入模式,就是精确到最后一位时,该怎么处理,这里CEILING表示向上取整
System.out.println(i);
}

可变长参数

定义函数参数时加三个‘.’

1
2
3
4
5
6
7
8
9
public class Person {
String name;
int age;
String sex;

public void test(String... strings){

}
}

在使用时可以传任意数量的实参

1
2
3
4
public static void main(String[] args) {
Person person = new Person();
person.test("1!", "5!", "哥们在这跟你说唱"); //这里我们可以自由传入任意数量的字符串
}

使用时strings为数组

String

如果我们仅仅是想要判断两个字符串的内容是否相同,不要使用==,String类重载了equals方法用于判断和比较内容是否相同:

1
2
3
4
5
public static void main(String[] args) {
String str1 = new String("Hello World");
String str2 = new String("Hello World");
System.out.println(str1.equals(str2)); //字符串的内容比较,一定要用equals
}

StringBuilder

这个类型是专门用于构造字符串的,我们可以使用它来对字符串进行拼接、裁剪等操作,它就像一个字符串编辑器,弥补了字符串不能修改的不足:

1
2
3
4
5
6
public static void main(String[] args) {
StringBuilder builder = new StringBuilder(); //一开始创建时,内部什么都没有
builder.append("AAA"); //我们可以使用append方法来讲字符串拼接到后面
builder.append("BBB");
System.out.println(builder.toString()); //当我们字符串编辑完成之后,就可以使用toString转换为字符串了
}

StringBuilder类的编辑操作也非常多,这里就不一一列出了。

泛型

泛型类

1
2
3
4
5
6
7
8
9
10
11
12
public class Score<T> {   //泛型类需要使用<>,我们需要在里面添加1 - N个类型变量
String name;
String id;
T value; //T会根据使用时提供的类型自动变成对应类型

public Score(String name, String id, T value) {
//这里T可以是任何类型,但是一旦确定,那么就不能修改了
this.name = name;
this.id = id;
this.value = value;
}
}

如果要让某个变量支持引用确定了任意类型的泛型,那么可以使用 ? 通配符:

1
2
3
4
5
6
public static void main(String[] args) {
Test<?> test = new Test<Integer>();
test = new Test<String>();
Object o = test.value;
//但是注意,如果使用通配符,那么由于类型不确定,所以说具体类型同样会变成Object
}

泛型只能确定为一个引用类型,基本类型是不支持的

泛型方法

1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String[] args) {
String str = test("Hello World!");
}

private static <T> T test(T t){ //在返回值类型前添加<>并填写泛型变量表示这个是一个泛型方法
return t;
}
}

泛型约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Score<T extends Number> {   //设定类型参数上界,必须是Number或是Number的子类
private final String name;
private final String id;
private final T value;

public Score(String name, String id, T value) {
this.name = name;
this.id = id;
this.value = value;
}

public T getValue() {
return value;
}

缓冲字节流:

  • BufferedInputStream:
    要创建一个缓冲字节流,只需要将原本的流作为构造参数传入BufferedInputStream即可:
1
2
3
4
5
6
7
8
public static void main(String[] args) {
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"))){ //传入FileInputStream
System.out.println((char) bufferedInputStream.read());
//操作和原来的流是一样的
}catch (IOException e){
e.printStackTrace();
}
}

BufferedInputStream操作和原来的FileInputStream流是一样的

  • BufferedOutputStream:
1
2
3
4
5
6
7
8
public static void main(String[] args) {
try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("output.txt"))){
outputStream.write("lbwnb".getBytes());
outputStream.flush();
}catch (IOException e) {
e.printStackTrace();
}
}

多线程

多线程的实现方式:

继承Thread类:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

public class Demo01 {
public static void main(String[] args) {
//创建线程
MyThread t01 = new MyThread();
MyThread t02 = new MyThread();
MyThread t03 = new MyThread("线程03");

//开启线程
t01.run();
t02.run();
t03.run();
// 不会启动线程,不会分配新的分支栈。(这种方式就是单线程。)
// start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
// 这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
// 启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
// run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
t01.start();
t02.start();
t03.start();
//设置线程名(补救的设置线程名的方式)
t01.setName("线程01");
t02.setName("线程02");
//设置主线程名称
Thread.currentThread().setName("主线程");
for (int i = 0; i < 50; i++) {
//Thread.currentThread() 获取当前正在执行线程的对象
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
class MyThread extends Thread{
public MyThread() {
}

public MyThread(String name) {
super(name);
}

//run方法是每个线程运行过程中都必须执行的方法
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(this.getName() + ":" + i);
}
}
}

实现Runnable接口:

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
29
30
31
32
33

public class Demo02 {
public static void main(String[] args) {
MyRunnable myRun = new MyRunnable();//将一个任务提取出来,让多个线程共同去执行
//封装线程对象
Thread t01 = new Thread(myRun, "线程01");
Thread t02 = new Thread(myRun, "线程02");
Thread t03 = new Thread(myRun, "线程03");
//开启线程
t01.start();
t02.start();
t03.start();
//通过匿名内部类的方式创建线程
new Thread(new Runnable() {
//可以使用lambda表达式简化
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
},"线程04").start();
}
}
//自定义线程类,实现Runnable接口
//这并不是一个线程类,是一个可运行的类,它还不是一个线程。
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}

线程锁和线程同步

synchronized关键字也可以作用于方法上,调用此方法时也会获取锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static int value = 0;

private static synchronized void add(){
value++;
}

public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) add();
System.out.println("线程1完成");
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) add();
System.out.println("线程2完成");
});
t1.start();
t2.start();
Thread.sleep(1000); //主线程停止1秒,保证两个线程执行完成
System.out.println(value);
}

注解 = 特性

1
2
3
4
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) 
public @interface Test {
String value();
}

—end—