【JavaGuide笔记】2.1 Java 基础

2020-03-24  本文已影响0人  linyk3

原文:JavaGuide

2.1 Java 基础

1. 面向对象和面向过程的区别

注意: Java 性能差的主要原因不是因为它是面向对象语言,而是因为Java是半编译语言,最终执行的代码并不是CPU直接执行的二进制。

2. Java 语言有哪些特点

3. JVM、JDK、JRE

JVM

Java虚拟机(JVM)是运行Java字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows、Linux、MacOS),目的是使用相同的字节码,他们都会给出相同的结果。

Java字节码

在Java中,JVM可以理解的代码就叫做字节码(扩展名为.class的文件),它不面向任何特定的处理器,只面向虚拟机。
Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,并且无序重新编译便可在不同操作系统的计算机上运行。

Java 代码编译:


image.png

Java字节码 == JVM ==> 二进制机器码时,JVM类加载器首先加载字节码文件,然后通过解释器逐行解释执行。这种方式的执行速度相对比较慢。而且有些方法和代码块是经常需要被调用的(热点代码),所以后面引进了JIT编译器。JIT属于运行时编译,JIT完成一次编译后,会将字节码对应的机器码保存下来,下次可以直接使用。

Java字节码和不同系统的JVM实现是Java语言“Write Once,Run Anywhere”的关键所在。

JDK 和 JRE

JRE:Java运行时环境。它是运行已编译Java 程序所需的所有内容的集合。包括Java虚拟机(JVM)、Java类库、Java命令和其他一些基础构件。但是它不能用于创建新程序。

JDK:Java Development Kit,它是功能齐全的Java SDK。他包含Java运行环境(JRE)、编译器(javac)和工具(javadoc和jdb)。JDK能够创建和编译程序。

4. Oracle JDK 和 OpenJDK的对比

对于Java 7, 这两个没有关键不同的地方。
OpenJDK 是基于Sum捐赠的HotSpot源代码。

5. Java 和 C++ 的区别

6.Java程序的主类

一个程序可以有多个类,但是只能有一个类是主类,包含main()
方法。

7、Java 应用程序 VS Java 小程序

8. 字符型常量 VS 字符串常量

字符型常量 字符串常量
形式 单引号表示的一个字符,例如 'k' 双引号引起的若干个字符,例如"linyk3"
含义 相当于一个整型值,可以参加表达式运算 代表一个地址,表示该字符串在内存中存放的位置
内存大小 Java中char占2个字节 若干个字节

注意:Java中每一种基本类型所占的存储空间是不变的:


image.png

9. 构造器Constructor 能否被重写override?

Constructor 不能被override(重写),但是可以被overload(重载),所以你看到一个类中有多个构造函数的情况。

10.重写和重载的区别

11.Java 面向对象编程的三大特性: 封装、继承、多态

封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法。

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以使用父类的可继承的所有功能。通过继承我们可以非常方便的复用以前的代码。

多态

多态指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。

在Java 中有两种形式可以实现多态:

12.String、StringBuffer 和 StringBuilder

String :
Java 9 之前: private final char[] value
Java 9 之后: private final byte[] value

StringBuilder 和 StringBuffer 都继承自 AbstractStringBuilder 类,char[] value,没有使用 final 关键字。

每次对String类型进行改变的时候,都会生成一个新的String对象,然后将变量指向新的String对象。StringBuffer 每次都会对自己进行操作,而不是生成新的对象。

特性 String StringBuffer StringBuilder
可变性 不可变 可变 可变
线程安全型 不可变常量,线程安全 同步锁,线程安全 非线程安全
性能 性能慢 性能快 比StringBuffer性能提升10%-15%

总结:

13.自动装箱和拆箱

14.静态方法内调用一个非静态成员是非法的

静态方法可以通过类调用,不必通过生成对象来进行调用。
而非静态成员需要通过对象来调用。

15.Java定义一个无参构造方法的作用

Java 程序在执行子类的构造方法之前,如果没有用 super()来调用父类特定的构造方法,则会调用父类中无参构造方法,因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中没有用super() 来调用父类中特定的构造方法,则编译时间发生错误:Java程序在父类中找不到无参构造方法可供执行。
解决方案就是在父类中加上一个不做事且没有参数的构造方法。

16. 略过

17. 接口和抽象类

备注:
1.JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。 如果直接实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。
2.JDK9中,接口被允许定义私有化方法。

Java 接口在JDK7 => JDK9 的变化:

18.成员变量 VS 局部变量

成员变量: 属于类,可以被public、private、static等修饰符所修饰。
局部变量: 方法中定义的变量或方法的参数,不能被访问控制修饰符及static修饰。
但是成员变量和局部变量都能被final所修饰。

成员变量如果是static 修饰,则属于类的变量,如果没有使用staitc修饰,则属于实例对象的。对象是存在于堆内存,局部变量存在于栈内存。

成员对象是对象的一部分,随对象的创建而存在,而局部变量随着方法的调用而自动消失。

如果成员变量没有被赋初始值,则会自动以类型的默认值赋值(如果是final,则必须被显式的赋值)。局部变量不会自动赋值。

19.创建对象

使用 new 运算符来创建对象实例。对象实例存储在堆内存中,对象引用存储在栈内存中,对象引用指向对象实例。

20.返回值

方法的返回值:指我们获取都的某个方法中的代码执行后产生的结果
返回值的作用:接收结果,并用于其他操作

21. 构造方法

类的构造方法:作用是完成对类对象的初始化工作。
如果一个类么有声明构造方法,也可以正确执行。因为一个类即使没有声明构造方法,也会有默认的无参构造方法。

22. 构造方法的特性

23.静态方法 VS 实例方法

  1. 可以通过类或对象来调用静态方法:类名.方法名对象名.方法名
  2. 调用实例方法是能通过对象名.方法名,需要创建对象后才能调用。
    静态方法在访问本类的成员时,只能访问静态成员,而不允许访问实例成员变量和实例方法。实例方法无此限制。

24.对象的相等 VS 引用的相等

对象相等:内存中存放的内容是否相等
引用相等:指向的内存地址是否相等

25.子类构造方法里调用父类无参构造方法

在调用子类构造方法之前会调用父类无参构造方法,目的是为了帮助子类做初始化工作。

26. == VS equals

public class test {
    public static void main(String[] args) {
        String a = new String("ab");   // a 为一个引用
        String b = new String("ab");   // b 为另一个引用,内容一样,内存地址不一样
        String aa = "ab";    // 放在常量池中
        String bb = "ab";    // 从常量池中查找
        System.out.println(a == b);       // false
        System.out.println(aa == bb);     // true 
        System.out.println(a.equals(b));  // true
        System.out.println(42 == 42.0);   // true
    }
}

String 的equals 方法是被重写过的,比较的是两个对象的内容。
当创建String类型的对象时,虚拟机在常量池中查找有没有已经存在的值, 如果有就直接赋值给当前引用, 没有就重新创建一个String对象。

27 hashCode 与 equals

hashCode() 默认的行为是对堆上的对象产生独特的值。如果没有重写hashCode(), 则该class的两个对象无论如何都不会相等。

28.Java中只有值传递

29.线程、进程、程序

程序:含有指令和数据的文件,程序是静态的代码。
进程:是系统运行的基本单位,是操作系统分配CPU时间、内存空间、文件和输入输出设备使用权的单位。
线程:线程是进程划分为更小的运行单位,一个进程可以包含1个或多个线程。

30. 线程基本状态

Java 线程在运行的生命周期有6种不同的状态:

状态名称 说明
new 初始状态,线程被构建,但是还没有调用start()方法
runnable 运行状态,Java线程将就绪和运行时笼统的成为运行中
blocked 阻塞状态,线程阻塞于锁
waiting 等待状态,线程进入等待状态,需要等待其他线程做出一些特定动作(通知或中断)
time_waiting 超时等待状态,相比于wating,它可以在指定时间内自行返回
terminated 终止状态,表示当前线程已经执行完毕
image.png

31.final 关键字

32. Java 异常

image.png

Java中,所有的异常都有一个共同的祖先:java.lang.Throwable 类。它有两个重要的子类Exception(异常)Error(错误)

异常处理:

finally 块不会被执行的情况:

注意:当try语句和finally语句都有return语句时,在方法返回之前,finally语句的内容将被执行,并且finally语句的返回值会覆盖原来的返回值:

public static int f(int value) {
      try {
           return value * value;
      } finally {
           if(value == 2) {
                  return 0;
           }
      }
}

如果调用f(2), 返回值将是0.

33.Java 序列化

当一个类实现了serializable接口,如果没有显式的定义 serialVersionUID,Java 序列化机制会根据编译的Class自动生成一个serialVersionUID,用作序列化版本比对。如果Class文件没有发生变化,就算编译多次, serialVersionUID 也不会变化。
如果我们不想通过编译来强制划分软件版本,集实现序列化接口的实体能够兼容先前的版本,就需要显式的定义序列化版本UID,这样即使Class 文件变化了,只要serialVersionUID的值不变,就可以进行正确的序列化和反序列化。
private static final long serialVersionUID = 1L

对于不想进行序列化的变量,可以使用 transient 关键字修饰。
transient:阻止实例中用此关键字修饰的变量序列化。当反序列化时,被修饰的变量不会被持久化和恢复。
只能修饰变量,不能修饰类和方法。

34. 获取键盘输入的两种方法

方法1. 通过Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
方法2. 通过BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();

35.Java中IO流

Java中IO流分为几种?

Java IO流共涉及40多个类,都是从如下4个抽象类基类中派生出来的:

既然有了字节流,为什么还要有字符流

不管是文件读写还是网络发送和接收,信息的最小存储单元都是字节。
字符流是由Java虚拟机将字节转换得到的,但是转换过程非常耗时,并且很容易因为编码类型出现编码问题,所有干脆提供直接操作字符的接口,方便平时对字符进行流操作。
如果是音频文件、图片等媒体文件,建议用字节流;
如果涉及到字符,建议用字符流。

BIO,NIO,AIO有什么区别

36. 常见关键字总结:final/static/this/super

final 关键字

final 关键字主要用在3个地方:变量、方法、类。

static 关键字

static 关键字主要有以下四种使用场景:

// 静态内部类实现单例模式
public class Singleton {
    // 声明为 private 避免调用默认构造方法创建对象
    private Singleton() {}

    // 声明为private,表明静态内部类只能在Singleton类中访问
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}
this 关键字

this 关键字用于引用类的当前实例,例如

class Manager {
    Employees[] employees;
    void manageEmployees() {
        int totalEmp = this.employees.length;
        System.out.println("Total employees: " + totalEmp)'
        this.report();
    }

    void report() { }
}

上面的例子中,this关键字用于两个地方:

super 关键字

super 关键字用于从子类访问父类的变量和方法,例如:

public class SupperClass {
    protected int number;
    protected showNumber() {      
        System.out.println("number = " + number);
    }
}

public class SubClass extends SupperClass {
    void bar() {
        super.number = 10;
        super.showNumber();
    }
 }

上面的例子中,SubClass 访问父类的成员变量number 并调用父类的showNumber() 方法。

注意:在构造方法中使用super() 调用父类中的其他构造方法时,该语句必须处于构造器的首行,否则编译器会报错。另外,this调用本类中其他构造方法时,也要放在首行。
this 和 super 不能用在static 方法中。

37.Collections 工具类和Arrays 工具类常见方法总结

Collections 工具类常用方法
排序操作
void reverse(List list); // 反转
void shuffle(List list); // 随机排序
void sort(List list); // 按照自然排序的升序
void sort(List list, Comparator c); // 定制排序,由Comparator控制排序逻辑
void swap(List list, int i, int j); // 交换两个索引位置的元素
void swap(List list, int distance); // 旋转,当distance为整数,将list后distance个元素整体移到前面,当distance为负数,将list的前distance个元素整体移到后面。
查找、替换操作:
int binarySearch(List list, Object key); // 二分查找,返回索引,注意list要求是有序的
int max(Collection coll); // 根据元素的自然顺序,返回最大的元素
int max(Collection coll, Comparator c); // 根据定制顺序,返回最大的元素
void fill(List list, Object obj); //用指定元素obj替换list中所有元素
void frequency(Collection c, Object o) ; // 统计元素c出现的次数
void indexOfSubList(List list, List target); // 统计target在list中第一次出现的索引,找不到就返回-1. 类比于 int lastIndexOfSubList(List list, List target)
boolean replaceAll(List list, Object oldVal, Object newVal); // 用新元素替换旧元素
同步控制

Collections 提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题。不过最好不要使用,英文效率非常低,建议使用JUC包下的并发集合。
HashSet、TreeSet、ArrayList、LinkedList、HashMap、TreeMap 都是线程不安全的。

synchronizedCollection(Collection<T> c); // 返回线程安全的collection
synchronizedList(List<T> list); // 返回线程安全的list
synchronizedMap(Map<K,V> map); // 返回线程安全的map
synchronizedSet(Set<T> set); // 返回线程安全的set;

Collections 还可以设置不可变集合:

// Collection.emptyXxx(); 创建一个空的、不可改变的Xxx对象
List<Object> list = Collections.emptyList();
Set<Object> set = Collections.emptySet();
Map<Object, Object> map = Collections.emptyMap();

ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1);
arrayList.add(3);
arrayList.add(3);
HashSet<Integer> integers1 = new HashSet<>();
integers1.add(1);
integers1.add(3);
integers1.add(2);

//Collections.emptyXXX();创建一个空的、不可改变的XXX对象
List<Object> list = Collections.emptyList();  // []
Set<Object> objects = Collections.emptySet(); // []
  Map<Object, Object> objectObjectMap = Collections.emptyMap(); // {}

//Collections.singletonXXX();
List<ArrayList<Integer>> arrayLists = Collections.singletonList(arrayList);  // [[1,3, 3]]
Set<ArrayList<Integer>> singleton = Collections.singleton(arrayList); // [[1,3]]
Map<String, String> nihao = Collections.singletonMap("1", "你好"); //{1=你好}

//unmodifiableXXX();创建普通XXX对象对应的不可变版本
List<Integer> integers = Collections.unmodifiableList(arrayList);  // [[1,3, 3]]
Set<Integer> integers2 = Collections.unmodifiableSet(integers1); //[1, 2, 3]

//添加出现异常:java.lang.UnsupportedOperationException
// list.add(1);
// arrayLists.add(arrayList);
// integers.add(1);
Arrays 类的常见操作
int a[] = { 1, 3, 2, 7, 6, 5, 4, 9 };
Arrays.sort(a); // sort(int[] a)方法按照数字顺序排列指定的数组。
Arrays.sort(a, 2, 6);  // sort(int[] a,int fromIndex,int toIndex)按升序排列数组的指定范围
Arrays.parallelSort(c);  // parallelSort(int[] a) 按照数字顺序排列指定的数组(并行的)。同sort方法一样也有按范围的排序
char d[] = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.parallelSort(d); // parallelSort给字符数组排序,sort也可以
String[] strs = { "abcdehg", "abcdefg", "abcdeag" };   
Arrays.sort(strs); // parallelSort给字符串排序,sort也可以
System.out.println(Arrays.toString(strs));//[abcdeag, abcdefg, abcdehg]
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.sort(e);  // 排序后再进行二分查找,否则找不到
int s = Arrays.binarySearch(e, 'c');
System.out.println("字符c在数组的位置:" + s);
char[] e = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
char[] f = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
Arrays.equals(e, f) // true
int[] g = { 1, 2, 3, 3, 3, 3, 6, 6, 6 };
Arrays.fill(g, 3);  //333333333
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
Arrays.fill(h, 0, 2, 9); // 993333666 数组中指定范围元素重新分配值  
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
char[] k = { 'a', 'f', 'b', 'c', 'e', 'A', 'C', 'B' };
System.out.println(Arrays.toString(k));// [a, f, b, c, e, A, C, B]
int[] h = { 1, 2, 3, 3, 3, 3, 6, 6, 6, };
int i[] = Arrays.copyOf(h, 6);   //123333  copyOf 方法实现数组复制,h为数组,6为复制的长度
// copyOfRange将指定数组的指定范围复制到新数组中
int j[] = Arrays.copyOfRange(h, 6, 11); //结果66600(h数组只有9个元素这里是从索引6到索引11复制所以不足的就为0)

38.深拷贝 VS 浅拷贝

39. 补充: 抽象类 VS 接口

参考:深入理解Java的接口和抽象类

1 抽象方法

抽象方法是一种页数的方法,它只有声明,没有具体的实现。

abstract void fun()
2 抽象类

如果一个类含有抽象方法,就称这个类是抽象类。抽象类在类前用 abstract 关键字修饰。

[public] abstract class ClassName {
    abstract void fun();
}

抽象类就是为了继承而存在的。
包含抽象方法的类成为抽象类,但抽象类中也可以有非抽象方法。抽象类和普通方法一样,可以拥有成员变量和普通的成员方法。
抽象类和普通类的区别:

3. 接口

接口泛指提供给别人调用的方法或函数,接口是对行为的抽象。

[public] interface InterfaceName {

}

接口中可以含有变量和方法,但是接口中的变量和方法会被隐式的指定

4. 抽象类和接口的区别

类比:飞机和鸟是不同的事物,但有一个共性,都会飞行。
所以可以将飞机设计为一个类Airplane,将鸟设计为一个类Bird。但是不能将飞行也设计为类。可以将飞行设计为一个接口Fly,里面包含了飞行的方法fly().
不同类型的飞机和鸟直接继承Airplane 和 Bird类即可,然后根据需要去实现Fly 接口。
也就是说,一个类继承了抽象类,则子类必须是抽象类的种类。而接口实现表示的是有没有,具不具备这种行为。(能飞,则可以实现Fly接口,不能飞则不行)

设计层面,抽象类是作为很多子类的父类,是一种模板设计。
接口是一种行为规范,是一种辐射式设计。
对于抽象类,如果添加了新的方法,子类可以不进行变更。
如果接口进行了变更,则实现这个接口的类都必须进行相应的改动。

上一篇 下一篇

猜你喜欢

热点阅读