从C++快速入门Java
约 1929 个字 278 行代码 预计阅读时间 10 分钟
本章内容建立在掌握C++的基础上,为从C++到Java的快速入门
基础
首先回答一个问题,为什么Java的main要写在类里面?这个是第一次看到Java程序会觉得很奇怪的写法
这主要是因为Java中一切皆对象,不允许将方法或者变量暴露在类外
Java语言的语法和C/C++很接近,所以最基础的基本看一下就会了,这里只列举一些不太一样的东西:
* Java源文件(.java)是先编译成字节码(.class文件),再由Java虚拟机(JVM)解释执行,此外,JVM的即时编译器(JIT)会在运行时将热点代码(频繁执行的代码)编译为机器码,以提高性能。所以Java比纯解释的代码比如Python要快,但是一般又比纯编译的语言比如C++要慢。
* 主函数:Java里面是写成public static void main(String[] args)
,这个函数签名是固定的,类似于C的int main()
,它也是Java程序的入口程序
* 输入输出
* 输出:System.out.println
和System.out.printf
* 输入:需要用到Scanner
类型,要先导入java.util.Scanner
模块,比如:
import java.util.Scanner;
public class Main{
public static void main(String[] args){
Scanner scanner=new Scanner(System.in);
String name=scanner.nextLine(); //读取一行输入
int age=scanner.nextInt(); //读取一行输入并获取整数
//如果没有下一行代码,可能会警告:Resource leak: 'scanner' is never closed
scanner.close(); // 手动关闭
System.out.printf("%s的年龄是%d",name,age);
}
}
//输入:nana
// 18
//输出:nana的年龄是18
boolean
,不能对boolean
进行类型转换
* 数组的写法略有不同(其实写成C++那样也可以过编译,但是不推荐,像下面那样写更符合Java风格:
* 定义:要用new
创建,int[] arr=new int[5]
* 遍历:Java也有那个for each
形式的语法for(int item:arr)
,但注意Java不提供C++里面那种auto
自动推导类型,所以要写明类型
* 排序,java.util.Arrays
中提供了Arrays.sort()
* 输出,Java直接输出数组会输出哈希码,需要先用Arrays.toString()
转化成字符串
import java.util.Scanner;
import java.util.Arrays;
public class Main{
public static void main(String[] args){
int [] arr=new int[10];
Scanner scanner=new Scanner(System.in);
for(int i=0;i<arr.length;i++){
arr[i]=scanner.nextInt();
}
scanner.close();
System.out.print("输出是一个哈希码:");
System.out.println(arr);
for(int item:arr){
System.out.printf("%d ",item);
}
System.out.print("\n");
Arrays.sort(arr);
//注意是用Array提供的toString而不是写成arr.toString()
System.out.println(Arrays.toString(arr));
}
}
//输入1 2 3 4 5 6 7 8 9 10
/*输出
输出是一个哈希码:[I@6ea12c19
1 2 3 4 5 6 7 8 9 10
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*/
- 常量使用
final
- Java有垃圾回收机制,所以根本不需要手动管理内存,自然也就没有
delete
之类的东西 - Java多了一个文档注释
/** */
,这种注释可以被工具提取并生成API文档 - Java也没有指针,提供了引用,除了基本类型的变量名应该都是引用,在传参时也都是引用传递
- 类型转换
- 低转高是自动的
- 高转低必须强制类型转换
面向对象
封装
这部分也是和C++高度相似,Java同样也是有private
和public
以及protected
修饰符,有this
变量指向当前对象,构造函数与类同名且没有返回值(但是Java要写public
),通过new
创建新的对象。
继承
Java提供了extends
关键字来实现继承,所以没有C++里面的那种私有继承,所有继承一律都是public
的。Java可以通过super
调用父类方法,可以用final
禁止继承。Java不支持多继承
Java有一个万物起源的类,也就是object
,Java中一切类(除了object
本身)都是继承自object
。
和C++一样,upcasting
永远是安全的,downcasting
大概率会失败,Java提供instanceof
来检验一个变量是否是指定类型。
多态
Java支持函数重载,但不支持运算符重载,Java的Override提供了@Override
进行覆写检查,
Java所有方法都相当于C++里面的虚函数,支持全面的动态绑定
//基类
class Animal{
public void makeSound(){
System.out.println("Animal is making a sound");
}
}
//派生类1
class Dog extends Animal{
@Override
public void makeSound(){
System.out.println("Dog is barking");
}
}
//派生类2
class Cat extends Animal{
@Override
public void makeSound(){
System.out.println("Cat is meowing");
}
}
public class Main{
public static void main(String[] args){
//Upcasting
Animal myAnimal=new Animal();
Animal myDog=new Dog();
Animal myCat=new Cat();
myAnimal.makeSound();
myDog.makeSound();
myCat.makeSound();
}
}
//结果:
/*
Animal is making a sound
Dog is barking
Cat is meowing
*/
接口
Java中抽象类用abstract
修饰,抽象类是不能被实例化的类,通常用于作为其他类的基类。它可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。
abstract class Animal{
//抽象方法
public abstract void makeSound();
//具体方法
public void sleep(){
System.out.println("Sleeping...");
}
}
class Dog extends Animal{
@Override
public void makeSound(){
System.out.println("woof");
}
}
interface
,接口是一种完全抽象的类,它只能包含抽象方法(在Java 8之前)和常量(public static final变量)。从Java 8开始,接口可以包含默认方法(default methods)和静态方法(static methods)。
interface Animal {
void makeSound();
default void sleep(){
System.out.println("Sleeping...");
}
static void breathe(){
System.out.println("Breathing...");
}
}
包
相当于C++的命名空间,把功能相似或相关的类或接口组织在一个包中,方便类的查找和使用,不同包中的类名字可以相同。比如:
那么它的路径就应该是net/java/Something.java
但是源文件是有声明规则的,当在一个源文件中定义多个类,并且还有import
和package
语句时,需要特别注意这些规则:
- 一个源文件只能有一个
public
类,可以有多个非public
类,源文件名称应与public
类保持一致 - 如果一个类定义在某个包中,
package
语句应该放在第一行,import
语句紧随其后 import
和package
都是全局有效的,在同一个源文件中,不能给不同类不同的包声明
反射
这是一个新概念,是一个强大的特性,提供了动态操作类的能力,允许程序在运行时查询、访问和修改类、接口、字段和方法的信息。通常有以下工作流程: * 获取Class对象:首先获取目标类的Class对象 * 获取成员信息:通过Class对象,可以获取类的字段、方法、构造函数等信息 * 操作成员:通过反射API可以读取和修改字段的值、调用方法以及创建对象
Java提供了以下API:
java.lang.Class
:表示类的对象java.lang.reflect.Fielf
:表示类的对象java.lang.reflect.Method
:表示类的方法java.lang.reflect.Constructor
:表示类的构造函数
但是反射也有缺点:
- 破坏了封装性:你都能从外面获取类内部的信息了,可见封装被破坏了
- 性能开销大:反射的性能比直接写要差很多,因为是动态执行的,不好优化
class
是由JVM
在运行过程中动态加载的,JVM
在读到一种class
类型时,将其加载进内存,每加载一个class
,JVM
就为它创建一个Class
实例,并关联起来,一个Class
实例包含了class
的全部信息,所以如果我们获取了一个Class
实例,那么class
也就已知了,这个过程就叫做反射。
Java由于版本更新,这部分很多方法都已经弃用或者改进了,网上很多教程都过时了,建议真的得去编译器试试。
获取Class
有三种方法获取Class
:
// 直接访问 class 的 class 属性
Class<?> cls1 = String.class;
// 通过实例变量获取
String s = "Hello";
Class<?> cls2 = s.getClass();
// 知道完整类名
Class<?> cls3 = Class.forName("java.lang.String"); // 注意大小写
// Class 实例在 JVM 中是唯一的
boolean is_same = (cls1 == cls2); // true
System.out.println("cls1 == cls2: " + is_same); // 输出: cls1 == cls2: true
创建新对象
// 获取 String 的 Class 实例
Class<?> cls = String.class;
// 使用无参构造函数创建 String 实例
String s1 = (String) cls.getDeclaredConstructor().newInstance();
System.out.println("s1: " + s1); // 输出: s1:
// 如果需要使用带参构造函数,可以这样做:
Constructor<?> Constructor = cls.getConstructor(cls);
String s2 = (String) Constructor.newInstance("Hello, World!");
System.out.println("s2: " + s2); // 输出: s3: Hello, World!
获取类的信息
Class<?> clazz = String.class;
// 获取类名
System.out.println("类名: " + clazz.getName()); // 输出: java.lang.String
System.out.println("简单类名: " + clazz.getSimpleName()); // 输出: String
// 获取修饰符
int modifiers = clazz.getModifiers();
System.out.println("修饰符: " + Modifier.toString(modifiers)); // 输出: public final
// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("父类: " + superClass.getName()); // 输出: java.lang.Object
// 获取实现的接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> inter : interfaces) {
System.out.println("接口: " + inter.getName());
}
获取字段
class Person {
private String name;
public int age;
}
Class<?> clazz = Person.class;
// 获取所有 public 字段
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println("字段: " + field.getName()); // 输出: age
}
// 获取所有字段(包括 private)
Field[] allFields = clazz.getDeclaredFields();
for (Field field : allFields) {
System.out.println("字段: " + field.getName()); // 输出: name, age
}
// 访问和修改字段的值
Person person = new Person();
Field ageField = clazz.getField("age");
ageField.set(person, 25); // 设置 age 的值为 25
System.out.println("age: " + ageField.get(person)); // 输出: 25
// 访问 private 字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问
nameField.set(person, "Alice");
System.out.println("name: " + nameField.get(person)); // 输出: Alice
获取方法
class Calculator {
public int add(int a, int b) {
return a + b;
}
private void print(String message) {
System.out.println(message);
}
}
Class<?> clazz = Calculator.class;
// 获取所有 public 方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println("方法: " + method.getName());
}
// 获取所有方法(包括 private)
Method[] allMethods = clazz.getDeclaredMethods();
for (Method method : allMethods) {
System.out.println("方法: " + method.getName());
}
// 调用 public 方法
Calculator calculator = new Calculator();
Method addMethod = clazz.getMethod("add", int.class, int.class);
int result = (int) addMethod.invoke(calculator, 10, 20);
System.out.println("add 结果: " + result); // 输出: 30
// 调用 private 方法
Method printMethod = clazz.getDeclaredMethod("print", String.class);
printMethod.setAccessible(true); // 设置可访问
printMethod.invoke(calculator, "Hello, Reflection!"); // 输出: Hello, Reflection!
异常
这个也是和C++几乎是一样的,这里给一个自定义异常的例子
// 自定义异常类
class MyCustomException extends Exception{
public MyCustomException(String message){
super(message);
}
}
public class Main{
public static void main(String[] args){
try{
//调用一个抛出异常的方法
checkAge(16);
}catch(MyCustomException e){
System.out.println("捕获到异常:"+e.getMessage());
}
}
public static void checkAge(int age) throws MyCustomException{
if(age<18){
throw new MyCustomException("年龄不能小于18岁");
}
else{
System.out.println("年龄合法");
}
}
}
// 输出:捕获到异常:年龄不能小于18岁
数据结构
Java 提供了丰富的数据结构,类似于 C++ 的标准模板库(STL)。以下是 Java 中常见的数据结构及其与 C++ 的类比关系:
- Arrays:类似于 C++ 的静态数组。
- ArrayList:类似于 C++ 的
vector
,即动态数组。 - LinkedList:双向链表。
- Set
- HashSet:无序集合,底层实现为哈希表。
- TreeSet:有序集合,底层实现为红黑树,不允许重复元素。
- Map
- HashMap:无序映射,底层实现为哈希表。
- TreeMap:有序映射,底层实现为红黑树。
- Stack:类似于 C++ 的栈。
- Queue:类似于 C++ 的队列。Java 还提供了优先队列(
PriorityQueue
)和双端队列(Deque
)。 - TreeNode:Java 提供了树的节点类型,可能是由于 Java 没有指针,直接提供了树的节点类型。
- 枚举:Java 的枚举是类
Java 同样实现了常见的算法和迭代器
// 使用泛型的 ArrayList 示例
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("World");
// 使用泛型的 HashMap 示例
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
泛型
Java 的泛型(Generics)类似于 C++ 的模板(Templates),允许开发者编写通用的、类型安全的代码。通过泛型,可以在编译时进行类型检查,减少运行时错误。
泛型类
//泛型类
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
泛型函数
//泛型函数
public <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
通配符与边界
通配符
<?>
:表示任意类型<? extend T>
:表示T
或者T
的子类型<? super >
:表示T
或者T
的父类型
边界
可以限制参数的范围