随着互联网的不断发展,Java这门技术也越来越重要,很多人都开启了Java学习,本文就介绍了Java的基础内容。
类、接口、数组都属于引用数据类型,其变量值是引用数据类型的地址值。
如 Student stu = new Student();
stu为Student对象的地址值。
public class Demo {
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu);
// 输出 com.sysu.Student@5c647e05
// com.sysu为包名,Student为类名,5c647e05为16进制地址。
}
}
Java的自动装箱和拆箱是指在基本数据类型和对应的包装类之间自动进行转换的特性。
包装类:
自动装箱是指将基本数据类型转换为对应的包装类对象。例如,将int类型的值赋给Integer类型的变量时,会自动将int类型的值包装成Integer对象。
int num = 10;
Integer integer = num; // 自动装箱
自动拆箱是指将包装类对象转换为对应的基本数据类型。例如,将Integer类型的对象赋给int类型的变量时,会自动将Integer对象拆箱成int类型的值。
Integer integer = 10;
int num = integer; // 自动拆箱
自动装箱和拆箱可以使代码更简洁,不需要显式地进行类型转换。它们在Java编程中经常被使用,可以方便地在基本数据类型和包装类之间进行转换。
需要注意的是,自动装箱和拆箱可能会带来一些性能上的开销。在一些对性能要求较高的场景中,可以考虑手动进行装箱和拆箱操作,以避免不必要的性能损耗。
自动装箱自动拆箱原理:
例题:如何对Integer和Double类型判断数值相等
Integer i = 100;
Double d = 100.00;
System.out.println(i.doubleValue() == d.doubleValue());
// 不同的类无法直接进行比较,也无法调用类的compareTo方法进行比较
// 可将他们转为相同的基本数据类型进行比较
Java是一种面向对象的编程语言,面向对象是一种编程范式,它将程序组织成对象的集合,每个对象都有自己的属性和行为。
在Java中,一切都是对象。每个对象都是根据类定义的,类是对象的模板,描述了对象的属性和行为。通过创建类的实例(对象),可以使用对象的属性和行为来完成任务。
面向对象编程的核心概念包括:
面向对象编程的优点包括:
总之,Java是一种面向对象的编程语言,面向对象编程的核心概念包括类和对象、封装、继承和多态。面向对象编程可以提高代码的可重用性、可维护性、可扩展性和高效性。
static关键字:
static静态代码块:
static { // 需要执行的操作 }
final关键字:
Java中的抽象类和接口都是用于实现类的继承和实现。它们有一些共同点,但也有一些重要的区别。
共同点:
区别:
抽象类通常用于基于继承关系构建类的层次结构,提供一些通用的方法和状态,而接口则用于定义一组操作,可以被多个类实现。面向对象设计中,抽象类和接口都是很重要的概念,具体使用哪种方式取决于具体的需求和设计考虑。
Java中的抽象类是一种特殊的类,它不能被实例化,只能被继承。抽象类用abstract关键字来声明。抽象类可以包含抽象方法和非抽象方法。
抽象类的特点包括:
以下是抽象类一个简单的示例:
// 定义抽象类Animal
abstract class Animal {
// 抽象方法需要子类来实现
public abstract void makeSound();
// 非抽象方法可以有方法体
public void sleep() {
System.out.println("Animal is sleeping");
}
}
// 定义Animal的子类Dog
class Dog extends Animal {
// 实现抽象方法
public void makeSound() {
System.out.println("Dog is barking");
}
}
// 定义Animal的子类Cat
class Cat extends Animal {
// 实现抽象方法
public void makeSound() {
System.out.println("Cat is meowing");
}
}
// 在主程序中使用抽象类和子类
public class Main {
public static void main(String[] args) {
Animal dog = new Dog(); // 通过子类实例化对象
dog.makeSound(); // 调用抽象方法
dog.sleep(); // 调用非抽象方法
Animal cat = new Cat(); // 通过子类实例化对象
cat.makeSound(); // 调用抽象方法
cat.sleep(); // 调用非抽象方法
}
}
在上面的例子中,抽象类Animal定义了一个抽象方法makeSound()和一个非抽象方法sleep()。Dog类和Cat类是Animal类的子类,它们必须实现makeSound()抽象方法。在主程序中,我们通过子类实例化了Dog和Cat对象,并调用了它们的方法。通过抽象类的使用,我们可以将共性的行为抽象出来,提高代码的可维护性和可扩展性。
接口(Interface)是一种抽象的数据类型,用于定义一组方法的规范,而不包含具体的实现。接口定义了一组方法的签名,但没有提供方法的实现细节。其他类可以通过实现接口来实现这些方法。
接口的定义使用interface关键字,语法如下:
public interface 接口名 {
// 声明方法(可以是抽象方法、默认方法、静态方法)
}
接口的特点包括:
接口的主要作用:
以下是一个简单的接口示例:
interface Animal {
void makeSound(); // 抽象方法
// 默认方法
default void sleep() {
System.out.println("Animal is sleeping");
}
// 静态方法
static void eat() {
System.out.println("Animal is eating");
}
}
// 实现接口
class Dog implements Animal {
public void makeSound() {
System.out.println("Dog is barking");
}
}
// 使用接口
public class Main {
public static void main(String[] args) {
Animal dog = new Dog(); // 通过接口实例化对象
dog.makeSound(); // 调用抽象方法
dog.sleep(); // 调用默认方法
Animal.eat(); // 调用静态方法
}
在上面的示例中,Animal接口定义了一个抽象方法makeSound(),一个默认方法sleep()和一个静态方法eat()。Dog类实现了Animal接口并提供了makeSound()方法的具体实现。在主程序中,我们通过接口实例化了Dog对象,并调用了接口中的方法。通过接口的使用,我们可以在不直接关联具体类的情况下,调用对象的方法并实现多态性。
Java中的封装、继承和多态是面向对象编程的三个核心概念。
封装是将数据和操作数据的方法封装在一起,隐藏了对象的内部细节,只暴露必要的接口。
通过封装,可以保护对象的数据,提高代码的可维护性和可重用性。封装可以通过访问修饰符(如private、protected、public)来实现,将属性设置为私有的,通过公共的方法来访问和修改属性。
以下是一个简单的Java封装例子代码:
public class Person {
private String name;
private int age;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
在这个例子中,Person类有三个私有的实例变量:name、age和gender。通过使用公共的getter和setter方法,我们可以访问和修改这些变量。例如,可以使用person.getName()来获取一个人的姓名,使用person.setAge(25)来设置一个人的年龄。
继承是一种面向对象编程的重要概念。继承允许一个类继承另一个类的属性和方法,从而创建一个新的类。在Java中,使用关键字extends来实现继承。
下面是一个简单的Java继承的例子:
public class Animal {
public void eat() {
System.out.println("动物正在吃食物");
}
}
public class Dog extends Animal {
public void bark() {
System.out.println("狗在叫");
}
}
public class Cat extends Animal {
public void bark() {
System.out.println("猫在叫");
}
}
在这个例子中,Animal类是一个基类,它有一个eat()方法。Dog类是一个派生类,它继承了Animal类,并且还有一个额外的bark()方法。
通过继承,Dog类可以使用Animal类的eat()方法,同时还可以添加自己的方法bark()。这样可以避免重复编写相同的代码,并且使代码更加可维护和可扩展。
需要注意的是,继承的有很多缺点,所以实际开发中应该谨慎考虑是否使用继承:
为了避免继承的缺点,可以考虑使用其他的面向对象编程原则和技术,如组合、接口和多态,且使用继承之前应认真构思好层次结构,如(动物-猫、狗)等。这些技术可以提供更灵活和可扩展的代码结构。
多态允许一个对象在不同的情况下表现出不同的行为。在Java中,多态可以通过继承和接口来实现。
多态的前提:
多态的优点:
多态的缺点:
引用数据类型的转化:
强制类型转换检查:
Student student = new Student();
if (person instanceof Person){
student = (Student)person;
}
public class Animal {
public String name = "动物";
public void makeSound() {
System.out.println("动物发出声音");
}
}
public class Dog extends Animal {
public String name = "狗狗";
public void makeSound() {
System.out.println("狗在汪汪叫");
}
}
public class Cat extends Animal {
public String name = "小猫";
public void makeSound() {
System.out.println("猫在喵喵叫");
}
}
public class Demo {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
Dog dog = new Dog();
Cat cat = new Cat();
animal1.makeSound(); // 输出:狗在汪汪叫
animal2.makeSound(); // 输出:猫在喵喵叫
System.out.println(animal1.name) // 输出:动物
System.out.println(animal2.name) // 输出:动物
System.out.println(dog.name) // 输出:狗狗
System.out.println(cat.name) // 输出:小猫
}
}
在这个例子中,animal1和animal2对象的同一个方法表现了不同的行为,对象虽然是Animal对象,但它们分别指向了Dog类和Cat类的对象。当调用makeSound()方法时,实际上会调用派生类中重写的方法。
值得注意的是,子类并不会重写父类的属性,如上面的例子所示。
利用接口的特性实现多态性。接口定义了一组抽象方法,而实现这个接口的类必须提供方法的具体实现。通过使用接口,可以在不同的类中使用相同的方法名称,但具体的实现可能不同。
以下是一个示例代码:
interface Animal {
void makeSound();
}
class Dog implements Animal {
public void makeSound() {
System.out.println("狗在汪汪叫");
}
}
class Cat implements Animal {
public void makeSound() {
System.out.println("猫在喵喵叫");
}
}
class Main {
public static void main(String[] args) {
Animal animal1 = new Dog();
Animal animal2 = new Cat();
animal1.makeSound(); // 输出:狗在汪汪叫
animal2.makeSound(); // 输出:猫在喵喵叫
}
}
Java集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性。
但这样做带来如下两个问题:
Java的范型(Generics)提供了在编译时期对数据类型进行参数化的功能,使得代码更加灵活、类型安全和可重用。范型可以应用于类、接口和方法。
范型的主要优势有:
范型类的定义使用尖括号(<>)来指定类型参数,语法如下:
class 类名<类型参数1, 类型参数2, ...> {
// 成员变量、方法等
}
范型接口的定义使用和范型类类似的方式:
interface 接口名<类型参数1, 类型参数2, ...> {
// 方法定义
}
范型方法的定义使用类型参数的方式:
<类型参数> 返回类型 方法名(参数列表) {
// 方法体
}
范型的具体使用示例如下:
class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
public class Main {
public static void main(String[] args) {
Box<Integer> integerBox = new Box<>(); // 声明一个范型类对象,指定类型参数为Integer
integerBox.setItem(10); // 设置值
int value = integerBox.getItem(); // 获取值
System.out.println(value);
Box<String> stringBox = new Box<>(); // 声明一个范型类对象,指定类型参数为String
stringBox.setItem("Hello"); // 设置值
String strValue = stringBox.getItem(); // 获取值
System.out.println(strValue);
}
}
"?"是通配符(Wildcard)类型,表示未知的类型。它可以用在范型的声明、方法的参数、方法的返回值等位置。通配符可以用来表示任意类型,但在使用时有一些限制。
例如,可以使用List>表示一个类型未知的列表,在代码中可以读取列表的元素,但不能向列表中添加除了null之外的元素,因为不确定实际类型是否符合。
还可以使用"?"表示上界通配符或下界通配符。例如:
"T"是类型参数(Type Parameter),用于定义泛型类、泛型接口和泛型方法中的占位符类型。它可以表示任意的引用类型,比如T表示类型参数,可以将具体的类型传递给它。
序列化(Serialization)是指将对象转换成字节流的过程,以便在网络传输或存储到文件中。而反序列化(Deserialization)则是将字节流转换回对象的过程。
序列化和反序列化的主要目的
要实现对象的序列化,需要满足以下条件:
实现对象的序列化很简单,在Java中只需将对象写入到输出流中。例如:
import java.io.*;
class Student implements Serializable {
private static final long serialVersionUID = 1L; // 序列化版本号
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class Main {
public static void main(String[] args) {
Student student = new Student("Alice", 20);
// 序列化对象
try {
FileOutputStream fileOut = new FileOutputStream("student.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(student);
out.close();
fileOut.close();
System.out.println("Serialize student object successfully");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化对象
try {
FileInputStream fileIn = new FileInputStream("student.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
Student deserializedStudent = (Student) in.readObject();
in.close();
fileIn.close();
System.out.println("Deserialized student object:");
System.out.println("Name: " + deserializedStudent.getName());
System.out.println("Age: " + deserializedStudent.getAge());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
输出
Serialize student object successfully
Deserialized student object:
Name: Alice
Age: 20
上述示例中,我们定义了一个Student类,实现了Serializable接口,并标记为可序列化。然后我们创建了一个Student对象,并将其序列化到文件student.ser中。接着,我们从文件中反序列化出一个Student对象,并显示其字段值。
需要注意的是
Java中的反射(Reflection)是指在运行时动态地获取和操作类的信息。通过反射,我们可以获取类的构造函数、字段、方法等信息,并可以在运行时动态地创建对象、调用方法、访问或修改字段的值。
反射的应用场景包括:
然而,由于反射的开销较大,性能相对较低,使用时需要权衡利弊,并慎重处理异常。
值得注意的是,使用私有方法时,需要设置访问权限,可使用下面的函数进行访问权限控制。public void setAccessible(boolean flag)
默认false,设置为true则获取访问权限。
常见的反射操作:
Class.forName(String PakageAndClassName)
方法getClass()
方法.class
语法获取以下的是简单示例:
Class<?> clazz = Class.forName("com.example.MyClass");
Class<?> clazz = obj.getClass();
Class<?> clazz = MyClass.class;
可以使用Class对象的getConstructors()
方法获取所有公共构造函数,getDeclaredConstructors()
方法获取所有构造函数(包括私有的),还可以根据参数类型获取特定的构造函数。通过构造函数可以实例化对象。
例如:
Constructor<?>[] constructors = clazz.getConstructors();
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("example", 100);
可以使用Class对象的getFields()
方法获取所有公共字段,getDeclaredFields()
方法获取所有字段(包括私有的),还可以根据字段名获取特定字段。通过字段可以获取或设置对象的属性值。
例如:
Field[] fields = clazz.getFields();
Field[] fields = clazz.getDeclaredFields();
Field field = clazz.getField("fieldName");
Object value = field.get(obj);
field.set(obj, newValue);
可以使用Class对象的getMethods()
方法获取所有公共方法,getDeclaredMethods()
方法获取所有方法,还可以根据方法名和参数类型获取特定方法。通过方法可以调用对象的方法。
示例:
Method[] methods = clazz.getMethods();
Method[] methods = clazz.getDeclaredMethods();
Method method = clazz.getMethod("methodName", int.class);
Object result = method.invoke(obj, 100);
反射的底层机制涉及到Java虚拟机(JVM)的类加载、字节码解析和内存布局等方面。下面是反射的主要底层机制:
总结起来,反射的底层机制主要涉及类加载、字节码解析、内存布局和动态方法调用等方面,通过这些机制可以在运行时动态地获取和操作类的信息。
Tring to do better.