Java 与 C# 语法差异
基础语法的差异
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 区别:
-
重复元素:
List允许存储重复元素,而Set不允许重复元素。 -
元素的顺序:
List是有序的集合,它按照元素插入的顺序进行存储并且可以根据索引访问元素;而Set是无序的集合,它不保证元素的存储顺序,也不能通过索引访问元素。 -
底层实现方式:
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 区别
-
数据存储方式:
ArrayList基于动态数组实现,支持随机访问,即可以通过索引直接访问元素;LinkedList基于双向链表实现,不支持随机访问,只能通过迭代器或者从头/尾部遍历来访问元素。 -
插入和删除操作:对于
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 区别
-
内部实现:
HashSet使用哈希表来存储元素,它允许使用null作为元素,是一个无序的集合;TreeSet基于红黑树(一种自平衡的二叉查找树)实现,它不允许使用null作为元素,是一个有序的集合。 -
元素排序:
HashSet不保证元素的顺序,元素存储的顺序可能与插入顺序不同;TreeSet则根据元素的自然顺序或者提供的比较器进行排序,并且提供了一些额外的有序集合操作方法。 -
插入和检索效率:
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) { // 方法体 }