Java 与 C# 语法差异

Joni是只狗 / 2024-05-01 / 原文

基础语法的差异

1. 静态类

Java 静态类里面可以声明非静态的属性和方法,C# 静态类只能包含静态的属性和静态的方法等

public  class People {
     static String outerStaticField = "I'm static in OuterClass";

    // 静态内部类
    static class StaticNestedClass {
        String innerField = "嵌套内部静态类的非静态字段";
        static String staticFile="嵌套内部静态类的静态字段";
        public void printFields() {
            // 可以访问外部类的静态字段
            System.out.println(outerStaticField);
            // 但不能访问外部类的非静态字段,因为静态内部类不持有外部类的实例引用
            // System.out.println(outerNonStaticField); // 这会导致编译错误
            System.out.println(innerField);
        }
    }
}

 2. 自定义字符串的语法糖

C# 中可以使用$"....{变量}."  的语法糖去动态写字符串值,也可以用string.Format方式

string s = "123";
string a1 = $"{s}666";
string a2 = string.Format("{0}666",s);

Java只能通过 String.format 方式,并且C#是直接用{0}代替字符串,而Java是s%代表字符串变量, %d代表整数 等 

String s = "123";
String a = String.format("%s666", s);
StringBuilder 用法两者都是差不多,只是 C# 提供的方法多一些

3. foreach循环

C#的foreach

String[] names = { "James", "Larry", "Tom", "Lacy" };
foreach (string name in names)
{
    Console.WriteLine(name);
}

Java中没有foreach, 代替的是 增强型for循环

String[] names = { "James", "Larry", "Tom", "Lacy" };
for (String name : names) {
    System.out.println(name);
}

4. 获取时间

Java推荐使用 LocalDate 类

        //方式一:LocalDate类(需要 Java 8 及以上)
        // 获取当前日期        
        LocalDate date2 = LocalDate.now().plusDays(1); // 增加一天
        System.out.println(date2);
        // 获取完整日期时间(包含T) ps: 2024-04-28T16:39:38.342
        LocalDateTime fullDate = LocalDateTime.now();
        System.out.println(fullDate);
        // 格式化日期时间(不包含T)  ps:2024-04-28 16:39:38
        LocalDateTime fullDate2 = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDate = fullDate2.format(formatter);
        System.out.println(formattedDate);

        // 方式二:Date 类(Java 7 以及之前的版本)
        //  使用 Date 类(Java 7 以及之前的版本):
        Date date = new Date();
        // 使用 toString() 函数显示日期时间
        System.out.println(date);
        
        //方式三:使用 Calendar 类(所有版本中都可以使用,但是写法相对负责)
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE, 1); // 增加一天
        System.out.println(calendar.getTime());

 5. 数据结构

5.1  集合

C# 中只提供了List<T> 方式的集合,集合顺序是有序的,按顺序插入,还不能按照索引插入。

Java中提供了List和Set两种,List又分为ArrayList 和 LinkedList 。Set 分为 HashSet 和 TreeSet

ArrayList(最常用的集合):
特点: 动态数组,可变大小。
优点: 高效的随机访问和快速尾部插入。
缺点: 中间插入和删除相对较慢。
LinkedList: 特点: 双向链表,元素之间通过指针连接。 优点: 插入和删除元素高效,迭代器性能好。 缺点: 随机访问相对较慢。 HashSet: 特点: 无序集合,基于HashMap实现。 优点: 高效的查找和插入操作。 缺点: 不保证顺序。
TreeSet: 特点:TreeSet 是有序集合,底层基于红黑树实现,不允许重复元素。 优点: 提供自动排序功能,适用于需要按顺序存储元素的场景。 缺点: 性能相对较差,不允许插入 null 元素。

List 和 Set 区别:

  1. 重复元素:List 允许存储重复元素,而 Set 不允许重复元素。

  2. 元素的顺序:List 是有序的集合,它按照元素插入的顺序进行存储并且可以根据索引访问元素;而 Set 是无序的集合,它不保证元素的存储顺序,也不能通过索引访问元素。

  3. 底层实现方式:List 通常以数组或链表的形式实现,例如 ArrayList 和 LinkedList;而 Set 通常以哈希表的形式实现,例如 HashSet 和 TreeSet

   // 使用 List 存储重复元素
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("banana");
        list.add("apple");
        System.out.println(list);  // 输出: [apple, banana, apple]

        // 使用 Set 存储不重复元素
        Set<String> set = new HashSet<>();
        set.add("apple");
        set.add("banana");
        set.add("apple");  // 重复元素不会被加入
        System.out.println(set);  // 输出: [apple, banana]

ArrayList 和 LinkedList 区别

  1. 数据存储方式:ArrayList 基于动态数组实现,支持随机访问,即可以通过索引直接访问元素;LinkedList 基于双向链表实现,不支持随机访问,只能通过迭代器或者从头/尾部遍历来访问元素。

  2. 插入和删除操作:对于 ArrayList,在列表中间或头部插入/删除元素时,由于需要移动元素,性能略低于链表;对于 LinkedList,由于只需要调整节点的指针,插入/删除性能很好

        // 使用 ArrayList
        List<Integer> arrayList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            arrayList.add(i);
        }
        
        // 使用 LinkedList
        List<Integer> linkedList = new LinkedList<>();
        for (int i = 0; i < 10; i++) {
            linkedList.add(i);
        }

        // 访问元素
        System.out.println(arrayList.get(5)); // 输出: 5
        System.out.println(linkedList.get(5)); // 此处需要遍历链表,输出: 5

        // 插入和删除操作
        arrayList.add(5, 100); // 在索引为5的位置插入100
        linkedList.add(5, 100); // 在索引为5的位置插入100

        arrayList.remove(5); // 移除索引为5的元素
        linkedList.remove(5); // 移除索引为5的元素

HashSet 和 TreeSet 区别

  1. 内部实现:HashSet 使用哈希表来存储元素,它允许使用 null 作为元素,是一个无序的集合;TreeSet 基于红黑树(一种自平衡的二叉查找树)实现,它不允许使用 null 作为元素,是一个有序的集合。

  2. 元素排序:HashSet 不保证元素的顺序,元素存储的顺序可能与插入顺序不同;TreeSet 则根据元素的自然顺序或者提供的比较器进行排序,并且提供了一些额外的有序集合操作方法。

  3. 插入和检索效率:HashSet 插入、删除和查找元素的效率通常较高,平均时间复杂度为 O(1);TreeSet 对于插入、删除和查找元素的效率较低,平均时间复杂度为 O(logn),因为它需要维持红黑树的平衡。

  // 使用 HashSet
        Set<String> hashSet = new HashSet<>();
        hashSet.add("banana");
        hashSet.add("apple");
        hashSet.add("orange");
        System.out.println(hashSet);  // 输出:[orange, banana, apple]

        // 使用 TreeSet
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("banana");
        treeSet.add("apple");
        treeSet.add("orange");
        System.out.println(treeSet);  // 输出:[apple, banana, orange]

5.2 键值对

C# 中使用 Dictionary<Key, Value> 

Java使用  Map<Key,Value> 。其中分为 HashMap和 TreeMap

HashMap(最常用的):
特点: 基于哈希表实现的键值对存储结构。
优点: 高效的查找、插入和删除操作。
缺点: 无序,不保证顺序。
TreeMap: 特点: 基于红黑树实现的有序键值对存储结构。 优点: 有序,支持按照键的顺序遍历。 缺点: 插入和删除相对较慢。

基本使用

 // 创建一个新的 HashMap
        Map<String, Integer> map = new HashMap<>();

        // 添加元素
        map.put("apple", 10);
        map.put("banana", 20);
        map.put("orange", 15);

        // 读取元素
        System.out.println("获取键为 banana 的值:" + map.get("banana"));  // 输出:20

        // 修改元素
        map.put("apple", 25);

        // 输出Map内容
        System.out.println("修改后的Map内容:");
        System.out.println(map);  // 输出:{orange=15, apple=25, banana=20}

        // 移除元素
        map.remove("orange");

        // 输出Map内容
        System.out.println("移除元素后的Map内容:");
        System.out.println(map);  // 输出:{apple=25, banana=20}

        // 遍历元素
        System.out.println("遍历Map内容:");
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

 

6. 类型通配符:?

在C# 中没有这个类型通配符。

Java中相当于Object,只是比Object范围更广泛,?包括了Object。类型通配符更加灵活,并提供了更多的编译时类型安全性和约束,能够更好地适应泛型编程的需求.使用类型通配符时,编译器可以进行更严格的类型检查,确保类型的兼容性。而Object类型在编译时不提供类型检查,可能导致运行时的类型转换错误。

public class Printer {
    public void printValue(List<?> list) {
        for (Object item : list) {
            System.out.println(item);
        }
    }
}
public static void printValue2( List<? extends Number> list) {
        for (Object item : list) {
            System.out.println(item);
        }
}

 

 

 

面向对象语法

1. 继承

C# 不管是继承类还是接口直接使用:进行继承。

调用父级的方法使用base关键字。

如果需要重写需要在被继承的父类中写virtual,override,abstract 关键字,在重写的子类中用 override关键字

public class Parent
{
    public  void eat()
    {
        Console.WriteLine("Parent's logic");
    }
    public virtual void food()
    {
        Console.WriteLine("Parent's logic");
    }
}

public class Child : Parent
{
    public void test()
    {
        base.eat();
    }
    public override void food()
    {
        Console.WriteLine("Child's logic");
    }
}

public class GrandChild : Child
{
    // GrandChild 继承了 Child 类
    public override void food()
    {
        Console.WriteLine("Child's logic");
    }
}

 

在 Java 中,继承使用关键字extends 来继承父类, 使用 implements 关键字继承多个接口

调用父级的方法使用 super 关键字。

子类要重写父类的方法,只需要在子类中使用 @Override 注解来显式地标记即可

        class Parent {
            void printMessage() {
                System.out.println("Parent's logic");
            }
            void eat()
            {
                System.out.println("Parent's logic");
            }
        }
    
        class Child extends Parent {
            @Override
            void printMessage() {
                System.out.println("Child's logic");
                super.eat();
            }
        }
    
        class GrandChild extends Child {
            // GrandChild 继承了 Child 类
        }

 2. 泛型

c# 泛型方法约束条件是写在尖括号内的泛型参数后面的where子句中,具体来说,where子句是在方法的返回类型之后、紧跟在尖括号后的泛型参数列表之后的位置

public static T Maximum<T>(T x, T y, T z) where T : IComparable<T> {
    // 方法体
}

 

JAVA泛型方法的约束条件位置不一样,写在返回类型之前的尖括号中的。具体来说,约束条件紧跟在方法修饰符(如public static等)后的尖括号中,并用关键字"extends"指定泛型参数的边界

public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
    // 方法体
}