后端学习 - Java基础

后端学习 - Java基础,第1张

后端学习 - Java基础

学习 JavaGuide 的笔记。

文章目录
  • 一 基础概念
    • 1 有关Java
    • 2 JVM / JDK / JRE
    • 3 与C++的联系和区别
  • 二 Java语法
    • 1 各类型数据占用空间大小
    • 2 可变长参数
    • 3 静态方法与实例方法
    • 4 重载和重写
    • 5 泛型
    • 6 == 和 equals() 的区别
    • 7 hashCode() 方法
    • 8 包装类型
    • 9 final 关键字
    • 10 Java的参数传递机制
  • 三 Java的面向对象
    • 1 面向过程与面向对象
    • 2 对象与对象引用
    • 3 构造方法
    • 4 封装、继承、多态
    • 5 StringBuffer 与 StringBuilder
    • 6 引用拷贝、浅拷贝与深拷贝
    • 7 代理模式
  • 四 异常
    • 1 异常分类
    • 2 try - catch - finally 结构
    • 3 不执行 finally 的特殊情况
    • 4 使用 try-with-resources 代替 try-catch-finally
  • 五 I/O流
    • 1 transient 关键字
    • 2 通过键盘获取输入的两种方法
    • 3 I/O流分类
    • 4 为什么使用字节流
  • 六 反射
    • 1 什么是反射
    • 2 反射的实例

一 基础概念 1 有关Java
  • 编译型语言会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。
  • 解释型语言会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。
  • Java是编译与解释并存的语言。由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。字节码对应.class文件,它是面向JVM而非特定系统的,具有良好的可移植性。
2 JVM / JDK / JRE
  • JVM(Java virtual machine):运行 Java 字节码(.class格式文件)的虚拟机,针对不同系统有不同的实现。JVM是一种规范,满足规范的虚拟机都可称为JVM。
  • JDK(Java development kit):功能齐全的 Java SDK。拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。
  • JRE(Java runtime environment):Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是不能用于创建新程序。

总之,JDK 包含 JRE 包含 JVM。

3 与C++的联系和区别
  • 都是面向对象的语言,支持封装、继承、多态;
  • Java 不提供指针来直接访问内存,程序内存更加安全;
  • Java 的类是单继承的(接口支持多重继承),C++ 支持多重继承;
  • Java 有自动内存管理垃圾回收机制;
  • C ++同时支持方法重载和 *** 作符重载,但是 Java 只支持方法重载。
二 Java语法 1 各类型数据占用空间大小

Java中比较特殊的是char类型占用2字节(16bits)

2 可变长参数

下面的方法可以接受0或多个参数,可变长的形式参数只能放在参数列表的末尾,在实现时会被视为数组。

public static void printVariable(String... args) {
    for (String s : args) {
        System.out.println(s);
    }
}
3 静态方法与实例方法
  • 静态方法属于类,因此静态方法可以不创建实例使用。最好使用 类名.方法名 的方式调用。
  • 同样的,静态方法只能访问别的静态方法与静态成员变量,不能访问非静态成员。
4 重载和重写
  • 重载:相同的方法名,不同的参数列表。可以发生在一个类中,也可以发生在父类和子类间。重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。重载发生在编译时。
  • 重写:相同的方法名,相同的参数列表。发生在父类和子类间,本质上是子类覆盖了父类的方法。重写发生在运行时。

重写要实现的目的是,父类方法能完成的事,子类重写后不能完成得更差。具体指的是:抛出异常的类型更小或相等返回值的类型更小或相等(如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时可以返回该引用类型的子类),访问权限更大或相等

5 泛型
  • 泛型的本质是参数化类型,即数据类型被指定为一个参数。
  • Java 的泛型是伪泛型,因为 Java 在运行期间,所有的泛型信息都会被擦掉(类型擦除)
  • 具体使用时,有泛型类、泛型接口、泛型方法。
6 == 和 equals() 的区别
  • 对于基本数据类型,只能用 == 比较。
  • 对于引用数据类型, == 用于比较内存地址; equals() 如果未被重写,也是比较内存地址,重写后按照指定规则判断两个对象是否相等。
7 hashCode() 方法
  • 重写 equals() 方法时,必须同时重写 hashCode() 方法
  • 两个对象的 hashCode 值相等并不代表两个对象就相等(哈希碰撞)。两个对象相等则 hashCode 必相等。
  • 两个对象的比较,首先比较 hashCode() 的返回值是否相等,如果不相等直接认为两个对象不相等,如果相等则继续调用 equals() 方法,返回 True 时视为两个对象相等。
  • 如果重写 equals() 时没有重写 hashCode() 方法的话,可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
  • hashCode() 存在的意义是,减少 equals() 的调用,提高执行速度。
8 包装类型
  • 包装类型的比较必须用 equals() 。
  • 包装类型不赋值就是 Null ,而基本类型有默认值且不是 Null。
  • 基本数据类型直接存放在 Java 虚拟机栈中的局部变量表中,而包装类型属于对象类型,存在于堆中。相比于对象类型, 基本数据类型占用的空间非常小。 补充:局部变量表主要存放了编译期可知的基本数据类型对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。
  • Java 基本类型的包装类的大部分都实现了常量池技术。 Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。Float,Double 并没有实现常量池技术。
  • 装箱与拆箱:装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。
Integer i = 10;  //装箱,等价于 Integer i = Integer.valueOf(10)
int n = i;   //拆箱,int n = i.intValue()
9 final 关键字

被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。

10 Java的参数传递机制

Java只存在值传递,如果向方法传递引用类型,则在方法中产生引用类型的堆中的地址的拷贝。

public class Person {
    private String name;
   // 省略构造函数、Getter&Setter方法
}

public static void main(String[] args) {
    Person xiaoZhang = new Person("小张");
    Person xiaoLi = new Person("小李");
    swap(xiaoZhang, xiaoLi);
    System.out.println("xiaoZhang:" + xiaoZhang.getName());
    System.out.println("xiaoLi:" + xiaoLi.getName());
}

public static void swap(Person person1, Person person2) {
    Person temp = person1;
    person1 = person2;
    person2 = temp;
    System.out.println("person1:" + person1.getName());
    System.out.println("person2:" + person2.getName());
}

输出:
person1:小李
person2:小张
xiaoZhang:小张
xiaoLi:小李

是因为swap 方法的参数 person1 和 person2 只是拷贝的实参 xiaoZhang 和 xiaoLi 的地址。因此, person1 和 person2 的互换只是拷贝的两个地址的互换,不会影响到实参 xiaoZhang 和 xiaoLi 。

三 Java的面向对象 1 面向过程与面向对象
  • 面向过程 :面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
  • 面向对象 :面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
2 对象与对象引用
  • 使用 new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。
  • 对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相等。
3 构造方法
  • 一个类即使没有声明构造方法也会有默认的不带参数的构造方法。如果我们自己添加了类的构造方法(无论是否有参),Java 就不会再添加默认的无参数的构造方法了。
  • 如果重载了有参的构造方法,要把无参的构造方法也写出来(无论是否用到),可以帮助我们在创建对象的时候少踩坑。
  • 构造方法不能重写,但是可以重载。
4 封装、继承、多态
  • 封装:封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来 *** 作属性。
  • 继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有
  • 多态:具体表现为父类的引用指向子类的实例。引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;多态不能调用“只在子类存在但在父类不存在”的方法。
5 StringBuffer 与 StringBuilder
  • StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类。
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
  • 性能:StringBuilder > StringBuffer > String;安全性:String > StringBuffer > StringBuilder
  • 选择: *** 作少量的数据适用 String;单线程 *** 作字符串缓冲区下 *** 作大量数据适用 StringBuilder;多线程 *** 作字符串缓冲区下 *** 作大量数据适用 StringBuffer。
6 引用拷贝、浅拷贝与深拷贝
  • 引用拷贝:创建新的对象引用,指向原来的对象。
  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝 :深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
7 代理模式

静态代理

  • 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
  • 静态代理的实现步骤:定义一个接口及其实现类; 创建一个代理类同样实现这个接口,将目标对象注入进代理类(使其成为代理类的成员变量);然后在代理类的对应方法调用目标类中的对应方法。
  • 静态代理中,对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改且麻烦(需要对每个目标类都单独写一个代理类)。

JDK动态代理

  • 动态代理在运行时动态生成类字节码,并加载到 JVM 中的。
  • 动态代理更加灵活,不需要必须实现接口,可以直接代理实现类,并且可以不需要针对每个目标类都创建一个代理类。“一个代理类,到处使用”。

使用实例:

  1. 接口与接口的实现
public interface SmsService {
    String send(String message);
}

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}
  1. 动态代理类:动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法调用了被代理对象的原生方法。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DebugInvocationHandler implements InvocationHandler {
    
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }

	
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前,我们可以添加自己的 *** 作
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args); //调用的是method的invoke方法
        //调用方法之后,我们同样可以添加自己的 *** 作
        System.out.println("after method " + method.getName());
        return result;
    }
}
  1. 获取代理对象的工厂类:输入需要被代理的对象,输出其代理
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }
}
  1. 实际使用
// JdkProxyFactory.getProxy(new SmsServiceImpl()) 得到的是 Object 类型的代理类
// 将其转为接口类
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
// invoke() 方法: 当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。
smsService.send("java");
四 异常 1 异常分类
  • Checked exception 必须处理,Unchecked exception可以不处理。
  • 在编译过程中,如果受检查异常没有被 catch/throw 处理的话,就没办法通过编译 。
  • 除了 RuntimeException 及其子类以外,其他的Exception类及其子类都属于受检查异常 。
2 try - catch - finally 结构
  • try: 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块
  • catch块: 用于处理 try 捕获到的异常。
  • finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖原始的返回值。
3 不执行 finally 的特殊情况
  • 在 try 或 finally块中用了 System.exit(int)退出程序。但如果 System.exit(int) 在异常语句之后,finally 还是会被执行。
  • 程序所在的线程死亡。
  • 关闭 CPU。
4 使用 try-with-resources 代替 try-catch-finally

具体的做法是,在 try 关键字后增加括号,在其中创建资源对象(任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象),这样使得 catch 或 finally 块必须在资源关闭后执行,而不必显式关闭。

try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File("test.txt")));
             BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File("out.txt")))) {
            int b;
            while ((b = bin.read()) != -1) {
                bout.write(b);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }
五 I/O流 1 transient 关键字
  • 作用是阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。
  • transient 只能修饰变量,不能修饰类和方法。
  • transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0。
  • static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。
2 通过键盘获取输入的两种方法
  • Scanner
Scanner input = new Scanner(System.in);
String s  = input.nextLine();
  • BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
3 I/O流分类
  • 按照流的流向分,可以分为输入流和输出流;
  • 按照 *** 作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

4 为什么使用字节流

字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是非常耗时。并且,如果不知道编码类型,很容易出现乱码问题。

六 反射 1 什么是反射

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。

2 反射的实例

对于类:

package com.ys.reflex;
public class Person {
    //私有属性
    private String name = "Tom";
    //公有属性
    public int age = 18;
    //构造方法
    public Person() {
    }
    //私有方法
    private void say(){
        System.out.println("private say()...");
    }
    //公有方法
    public void work(){
        System.out.println("public work()...");
    }
}

获取类名的三种方式:

//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
//  类型的对象,而我不知道你具体是什么类,用这种方法
  Person p1 = new Person();
  Class c1 = p1.getClass();

//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
//  这说明任何一个类都有一个隐含的静态成员变量 class
  Class c2 = Person.class;

//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
//   但可能抛出 ClassNotFoundException 异常
  Class c3 = Class.forName("com.ys.reflex.Person");

Class 类具有如下的方法:

  • getName():获得类的完整名字。
  • getFields():获得类的public类型的属性。
  • getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  • getMethods():获得类的public类型的方法。
  • getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  • getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  • getConstructors():获得类的public类型的构造方法。
  • getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  • newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

调用以上方法:

//获得类完整的名字
String className = c2.getName();  // c2是Class类型
System.out.println(className);//输出com.ys.reflex.Person

//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
   System.out.println(field.getName());//age
}

//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
    System.out.println(field.getName());//name    age
}

//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
    System.out.println(method.getName());//work waid equls toString hashCode等
}

//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
    System.out.println(method.getName());//work say
}

//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);

//创建这个类的一个对象
Object p2 =  c2.newInstance();//调用无参的构造方法
//将 p2 对象的  f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob

//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
    System.out.println(constructor.toString());//public com.ys.reflex.Person()
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5696664.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存