java基础-面向对象+java语言基础

本文详细介绍了Java的三大特性:封装、继承和多态,以及反射机制的使用,包括Class类、反射操作和反射小结。同时,讨论了Java的基础知识,如数据类型、String的不变性、深拷贝与浅拷贝、关键字final和static、异常处理、序列化等。此外,还提及了Java8的新特性,如Lambda表达式和新日期API。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过了解java面向对象特性、java语言基础对java有一个入门认识


一、java特性

1. java三大特性

封装

数据被保护在对象中,用户无需知道对象内部细节,可以通过对象对外提供的接口来访问该对象。

继承

例如 Cat 和 Animal 是一种继承关系,因此 Cat 可以继承自 Animal,进而获得 Animal private 的属性和方法。

多态

多态前提:继承、重写、父类引用指向子类对象。
表现:调用方法时,可以动态判断去执行子类方法。

动态判断的表现:
假设 x 实例的class是D,此时调用一个方法 f(String)。如果 D 类定义了 方法 f(String) 就直接调用它,否则将在D 类的超类中寻找 f(String) 以此类推。

堆栈的变化:实现多态时,父类所有的成员会一同创建在堆中。
代码使用

转型解释
向上转型
父类 对象 =new 子类();

调用被重写的方法时,会调用子类的方法。
子类独特的方法不能被调用;父类独特的方法可以被调用
向下强转
if(pet instanceof Dog){ Dog dog = (Dog) pet;} else { Cat cat = (Cat)pet;}

强转类型时,要使用instanceof关键字
父类转子类是还原子类真面目:子类的所有成员可以被调用

如果 x 为 null , 进行下列测试 x instanceof C 不会产生异常, 只是返回 false。之所以这样处理是因为 null 没有引用任何对象, 当 然也不会引用 C 类型的对象。

请记住,只要 没有捕获 ClassCastException 异常,程序就会终止执行。 在一般情况下,应该尽量少用类型 转换和 instanceof 运算符。



2. 反射

2.1. 反射作用

反射是java动态语言的关键,反射机制允许程序在执行期借助反射API获取类的内部信息,并能直接调用对象的内部属性及方法。
在这里插入图片描述

2.2. Class类

在程序运行期间,JVM为所有对象维护了一个运行时标识。运行时标识跟踪着每个对象所属的类。
而Class类保存了所有对象的运行时标识。通过Class类可以访问任何类的内部信息,并能直接操作对象。

2.3. 反射的使用

通过2.2,我们知道通过Class类可以实现一些反射的操作。

//对属性进行操作
    private static void useClassFields() throws Exception{
        //1、getClass
        final String stuClassString = "com.practise.useOOP.Stu";
        Class<?> stuClass = Class.forName(stuClassString); //获取一个特定的Class实例
        //2、遍历所有变量属性
        final Field[] declaredFields = stuClass.getDeclaredFields();
        for (Field field : declaredFields) {
            System.out.println("name:" + field.getName() +
                    "; type:" + field.getType() +
                    "; modifiers:" + field.getModifiers());
        }

        //3、获取对象属性,并操作此属性
        Field ageField = stuClass.getDeclaredField("age");
        ageField.setAccessible(true);//设置私有变量可以赋值,但是破坏了java的封装性
        Object StuObject = stuClass.newInstance();
        ageField.set(StuObject, 12);
        System.out.print(ageField.get(StuObject));
    }
//对方法进行操作
    private static void useMethod() throws Exception {
        //1、getClass
        final String stuClassString = "com.practise.useOOP.Stu";
        Class<?> stuClass = Class.forName(stuClassString); //获取一个特定的Class实例
        //2、遍历方法的属性
        Method[] declaredMethods = stuClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println("name:" + method.getName() +
                    "; returnType:" + method.getReturnType() +
                    "; modifiers:" + method.getModifiers() +
                    "; parameterTypes" + Arrays.toString(method.getParameterTypes()));
        }

        //3、获取方法,并调用此方法
        Method running = stuClass.getDeclaredMethod("running");//有参数的时候需要传数据类型的Class对象,比如:int.class.
        Object stuObject = stuClass.newInstance();
        running.setAccessible(true);
        running.invoke(stuObject);
       //有参数的时候可以传参,有返回值的时候可以接收返回值。
    }
//操作构造器
    private static void useConstructor() throws Exception {
        //1、getClass
        //1、getClass
        String stuClassString = "com.practise.useOOP.Stu";
        //forName()反射获取类信息,并没有将实现留给了java,而是交给了jvm去加载。
        Class<?> stuClass = Class.forName(stuClassString); //获取一个特定的Class实例
        //2、getAllConstructor
        Constructor<?>[] declaredConstructors = stuClass.getDeclaredConstructors();
        for (Constructor constructor : declaredConstructors) {
            System.out.println("name:" + constructor.getName() +
                    "; modifiers:" + constructor.getModifiers() +
                    "; parameterTypes" + Arrays.toString(constructor.getParameterTypes()));
        }

        //3、获取一个构造器,并且通过一个构造器创建一个对象。
        Constructor<?> declaredConstructor = stuClass.getDeclaredConstructor(String.class, Integer.class, String.class);//设置属性的Class实例
        Object stuObject = declaredConstructor.newInstance("dada", 13, "running");
        System.out.println(((Stu) stuObject).getHobby());
    }

2.4. 反射小结

  1. 通过反射加载的类和方法,都是通过从列表中查找然后加载的,所以查询性能会随着类、方法的多少而变化。
  2. 反射的操作是线程安全的,因为是通过JVM加载生成的与线程无关。
  3. 反射使用ReflectionData缓存class信息,减少每次重新从JVM获取class带来的开销。
  4. 当知道需要的方法时,都会copy一份出来,不是使用原来的实例,从而保证数据隔离。
  5. 调用反射方法,最终是JVM执行invoke0()实现的。


二. java基础

1. 数据类型

1.1. 八个基本类型

  • boolean/1
  • byte/8
  • char/16
  • short/16
  • int/32
  • float/32
  • long/64
  • double/64

1.2 包装类与缓存池

new Integer(123) 与 Integer.valueOf(123) 的区别:

  • new Integer(123) 每次都会新建一个对象
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。

valueOf() 实现比较简单:先判断值是否在缓存池中,如果在的话就直接返回缓存池中的实例。

//在 Java 8 中,Integer 缓存池的大小默认为 -128~127。
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

如果在缓冲池之外:

//直接装箱,都重新new一个对象
Integer m = 323;
Integer n = 323;
System.out.println(m == n); // false


2. String

2.1. String的不变性

String 被声明为 final,因此它不可被继承。
内部使用 char 数组存储数据,该数组被声明为 final,也就是数组初始化之后不可变。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

2.2. 不可变的好处

好处描述
缓存hash值因为不可变所以计算的hash值也是不可变的,只需要一次计算
String Pool如果遇到相同字符的String对象,可以直接使用
线程安全

2.3. String 和StringBuilder、StringBuffer

  • String 字符串常量
  • StringBuffer 字符串变量(线程安全,内部使用 synchronized 进行同步)
  • StringBuilder 字符串变量(非线程安全)
场景解释
什么情况用“+”做字符串连接比append方法性能更好当连接后得到的字符串在静态存储区中是早已存在时
String的语法糖JVM会把s1的三段字符看成一个字符串,此时效率比StringBuffer高。在这里插入图片描述

2.4 String.intern() 保证同一对象



3. 深拷贝和浅拷贝

拷贝类型具体
浅拷贝只是复制了对象的引用地址,新旧对象指向同一个内存地址,修改其中一个对象的值,另一个对象的值随之改变。
深拷贝开辟新的内存空间,在新的内存空间里复制一个一模一样的对象,新老对象不共享内存,修改其中一个对象的值,不会影响另一个对象。

实现对象克隆

实现 Cloneable 接口并重写 Object 类中的 clone() 方法。

//实现
public class DeepCloneExample implements Cloneable {
    private int[] arr;
    。。。

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}

//使用
DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;
try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

clone的替代方案

使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常并且还需要类型转换。
Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。

public class CloneConstructorExample {
    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}

CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

 

4.关键字

4.1 final

变量对于基本类型,final 使数值不变;
对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
方法/类声明方法不能被子类重写。声明类不允许被继承。

4.2 static

静态变量: 又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它;静态变量在内存中只存在一份。

 

5. 异常

try-catch-finally
如果try{}里有一个return语句,finally{}里的code会在return前执行。

当在finally中改变返回值时会给程序造成困扰:
因为在return前,会记录下返回值,但这时会进入到finally中再修改返回值。

 

6. 序列化

网络传输所有类型的数据都会转换成字节流在网络上传送。所以在传输之前需要序列化。

6.1. 序列化与反序列化

序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。

6.2. 序列化实现方式

序号方式
1类实现Serializable接口(转换为字节流)
2将对象包装成JSON字符串(转换为字符流):
转Json工具有Jackson、FastJson(有漏洞)或者GJson
3protoBuf (转换为二进制)

6.3. 关于serialVersionUID

如果serialVersionUID没有显式生成,系统就会自动生成一个。如果类发生了变化(包括类名、接口名、方法、属性),系统会更改类的serialVersionUID。

serialVersionUID的工作机制是:
在序列化时系统会将将serialVersionUID写入到序列化文件中。
反序列化时,系统会先去检测文件中的serialVersionUID是否跟当前类文件的serialVersionUID是否一致,如果一直则反序列化成功,不一致则报错(如下)

所以需要显式声明serialVersionUID解决这种兼容性问题。

java.io.InvalidClassException: User; local class incompatible: stream classdesc serialVersionUID = -1451587475819212328, local class serialVersionUID = -3946714849072033140
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at Main.readUser(Main.java:32)at Main.main(Main.java:10)

6.4. 序列化需要注意的事项

  • 序列化只保存对象的状态,而不管对象的方法。
  • 当一个父类实现了序列化,它的子类也自动实现序列化。
  • 当一个实例对象引用其他对象,当序列化该对象时,也把引用的对象进行序列化。

 

7. java8新特性

  • Lambda表达式
  • 新的日期API
  • 引入Optional
  • 使用Base64
  • 接口的默认方法和静态方法
  • 新增方法引用格式
  • 新增Stream类

 

参考:
https://pdai.tech/md/java/basic/java-basic-lan-basic.html#final

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

roman_日积跬步-终至千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值