类与对象的定义:
Java:
在 Java 中,所有的代码都必须写在类中。Java 没有全局函数或全局变量。主函数(程序的入口点)必须在类中定义:
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");//输出用sout
}
}
输入需要用Java当中Scanner这一个类。
Scanner scanner = new Scanner(System.in);
int number = scanner.nextInt();
C++:
C++ 支持全局函数和全局变量。程序的入口点是全局定义的 main
函数,而不需要在类中定义:
输入的时候需要用std::cin
#include <iostream>
int main() {
int n;std::cin >> n;//输入
std::cout << "Hello, World!" << std::endl;//输出用cout
return 0;
}
数据类型:
Java: Java 有明确的基本数据类型(如 int
, char
, float
, boolean
等)。此外,Java 中的布尔类型(boolean
)只能是 true
或 false
。
C++: C++ 的基本数据类型也包括 int
, char
, float
等。现代 C++ 引入了 bool
类型。以前的c++0和1也表示false和true。
数组:
Java:
在 Java 中,数组是对象,必须使用 new
关键字来分配内存。数组的大小在声明时必须指定,不能动态改变。数组的类型和大小在声明时是固定的。
Java 自动初始化数组元素为其对应数据类型的默认值(例如 int
数组的元素默认初始化为 0
,boolean
数组默认初始化为 false
)。
int[] numbers = new int[5]; // 创建一个包含 5 个元素的 int 数组
//当然也可以写成int numbers[] = new int[5]但是在Java中会把中括号写前面表示数组对象
int[] initializedNumbers = {1, 2, 3, 4, 5}; // 使用大括号初始化数组
由于数组是对象,Java 中的数组存储在堆内存中,程序员不需要手动释放数组的内存,当数组不再被引用时,系统会自动回收其内存。
c++:
在 C++ 中,数组可以在声明时静态分配,或者使用动态内存分配(如 new
关键字)。
静态分配的数组大小在编译时必须确定,动态分配的数组可以在运行时确定大小。
静态数组元素没有自动初始化(除非是全局或静态数组),需要手动初始化或在声明时指定初始值。
int numbers[5]; // 静态分配的数组,大小为 5,元素未初始化
int initializedNumbers[5] = {1, 2, 3, 4, 5}; // 使用大括号初始化数组
int* dynamicArray = new int[5]; // 动态分配的数组,大小为 5
静态数组的内存是由编译器管理的,当数组的作用域结束时,内存会自动释放。
动态数组的内存是由程序员通过 new
分配的,需要使用 delete[]
关键字手动释放内存,否则会导致内存泄漏。
容器:
Java:
List(列表)
ArrayList
:基于动态数组实现,提供快速的随机访问能力,但在列表中间插入和删除元素的效率较低。
package ArrayListTest;
import java.util.ArrayList;
public class Test1 {
public static void main(String[] args) {
//1.创建集合的对象,存储的数据类型不能是基本数据类型
//只能是引用数据类型或者基本数据类型对应的包装类型
ArrayList<String> list = new ArrayList<>();
//添加元素
list.add("A");
list.add("B");
list.add("C");
list.add("D");
System.out.println(list);
//删除
list.remove("A");
System.out.println(list);
String s1 = list.remove(0);
System.out.println(s1);
//修改元素
String result = list.set(0, "E");
System.out.println(result);
System.out.println(list);
//查询元素
String s2 = list.get(0);
System.out.println(s2);
}
}
如果没有指定储存类型,那么就可以储存任意类型的对象。
import java.util.ArrayList;
public class Example {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 没有指定类型,等同于 ArrayList<Object>
list.add("Hello"); // 添加一个字符串
list.add(123); // 添加一个整数
list.add(new Object()); // 添加一个 Object 对象
}
}
LinkedList
:基于双向链表实现,随机访问较慢,但在任何位置的插入和删除操作都较快。
Set(集合)
Set
是一个不允许重复元素的集合。它是一种用来存储不重复元素的数据结构,你可以使用它来确保没有重复项。(c++同)
HashSet
:基于哈希表实现,优化了查找、插入和删除操作的速度,但不保证元素顺序。
TreeSet
:基于红黑树实现,元素按自然排序或自定义排序存储,查找效率高,但写入速度慢于 HashSet
。
Map(映射)
Map
是一种键值对的集合,它用于存储键与值之间的映射关系。每个键在 Map
中是唯一的,每个键最多只能映射到一个值。(c++同)
HashMap
:与 HashSet
类似,基于哈希表,提供快速访问,但不保持键的顺序。
TreeMap
:与 TreeSet
类似,基于红黑树,键按自然顺序或自定义顺序排序。
Queue(队列)
LinkedList
:除了实现 List
接口外,还实现了 Queue
接口,支持先进先出(FIFO)的数据处理。
PriorityQueue
:不是 FIFO 队列,它根据元素的自然顺序或构造时提供的 Comparator
来决定元素的顺序。
c++:
序列容器
std::vector
:动态数组的实现,提供快速的随机访问。与 Java 的 ArrayList
类似,但支持更底层的内存操作。
std::list
:双向链表,与 Java 的 LinkedList
类似,适合频繁的插入和删除操作。
std::deque
:双端队列,支持在头尾两端高效插入和删除,比 vector
更灵活。
关联容器
std::set
和 std::map
:基于红黑树,保证元素和键的排序,与 Java 的 TreeSet
和 TreeMap
相似。
std::multiset
和 std::multimap
:允许元素或键的重复,也是基于红黑树。
无序容器
std::unordered_set
和 std::unordered_map
:基于哈希表,与 Java 的 HashSet
和 HashMap
相似,提供快速访问,不保证元素或键的顺序。
容器适配器
std::stack
:后进先出(LIFO)的栈,通常基于 deque
或 vector
实现。
std::queue
:先进先出(FIFO)的队列,基于 deque
实现。
std::priority_queue
:基于堆实现的优先队列,与 Java 的 PriorityQueue
类似。
字符串处理:
Java:
Java 中字符串是对象,通过 String
类来表示,字符串是不可变的(即每次对字符串的修改都会产生一个新的字符串对象)。
StringBuffer
:
StringBuffer
是 Java 中的一个类,用于表示可以修改的字符序列。这个类提供了多种方法,可以在运行时安全地修改字符串内容,而无需创建新的未修改的字符串对象。由于字符串 (String
) 在 Java 中是不可变的,StringBuffer
提供了一个动态的、可变的替代方案。
常用方法
append(x)
:将数据 x
添加到当前 StringBuffer
的末尾。
insert(index, x)
:在指定位置 index
插入数据 x
。
delete(start, end)
:删除从 start
到 end
位置的内容。
reverse()
:将字符串内容反转。
toString()
:返回 StringBuffer
的字符串表示,即创建一个新的 String
对象,其中包含与 StringBuffer
相同的字符序列。
public class StringBufferExample {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World"); // 在末尾追加字符串
System.out.println(sb); // 输出 "Hello World"
sb.insert(5, " Java"); // 在指定位置插入字符串
System.out.println(sb); // 输出 "Hello Java World"
sb.delete(5, 10); // 删除从索引5到10的字符
System.out.println(sb); // 输出 "Hello World"
sb.reverse(); // 字符串反转
System.out.println(sb); // 输出 "dlroW olleH"
}
}
StringBuilder:
如果从功能和API的角度来看,StringBuffer
和 StringBuilder
在 Java 中非常相似。
常用方法:
append(x)
:向 StringBuilder
的末尾添加数据 x
。
insert(index, x)
:在指定位置 index
插入数据 x
。
delete(start, end)
:删除指定范围的内容。
reverse()
:将字符序列反转。
toString()
:返回 StringBuilder
内容的字符串表示形式。
StringJoiner:
StringJoiner
是专门用于构造由分隔符分隔的字符串序列的。允许你在结果字符串的开始和结束添加前缀和后缀。
常用方法:
add(e)
:添加一个元素 e
。
setEmptyValue(defaultValue)
:设置当序列为空时使用的默认值。
toString()
:返回完成构建的字符串。
总结
使用 String
当字符串操作简单且不频繁改变时。
使用 StringBuilder(单线程环境)/StringBuffer
(多线程环境)当需要高效地改变单个字符串,尤其是在循环或多次操作中。
使用 StringJoiner
当你需要构造有特定格式(如分隔符、前缀和后缀)的字符串序列时。
C++:
C++ 中的字符串可以使用字符数组(C 风格的字符串)或 std::string
类。std::string
提供了类似 Java String
的功能,但字符串是可变的。
方法(函数)
Java中的方法必须定义在类或接口中。
package day2;
import java.util.Scanner;
public class way {
public static void main(String[] args) {
int arr[] = new int[5];
Scanner sc = new Scanner(System.in);
for (int i = 0; i < arr.length; i++) arr[i] = sc.nextInt();
test(arr);
System.out.println(arr[0]);
}
//定义一个方法
public static void test(int arr[]) {
for (int i : arr) {
System.out.print(i + " ");
}
//可以直接改变main函数当中的值
arr[0] = 10;
System.out.println();
}
}
在 Java 中,当你将变量传递给方法时,传递方式取决于变量的数据类型:基本数据类型(如 int
, double
, boolean
等)是通过值传递的,而对象引用(如数组、对象等)是通过引用传递的,也就是可以在方法当中改变对象的值的。
类和对象:
类(Class)
类是一个模板或蓝图,它定义了一组具有相同属性(数据)和方法(行为)的对象的特征。可以把类比作建筑的设计图纸,它描述了建筑将会有哪些部分(比如房间、门窗)和功能(比如供暖、照明)。
在 Java 中,类通过关键字 class
定义,它包含了两大部分:
属性:这些是类中定义的变量,用于存储对象的状态或数据。
方法(也叫做成员函数):这些是类中定义的函数,用于定义对象可以执行的操作。
public class Car {
// 属性
String color;
int year;
// 方法
void accelerate() {
System.out.println("Car is accelerating.");
}
}
对象(Object)
对象是类的一个实例。如果说类是设计图纸,那么对象就是根据这个图纸建造的实际建筑。每个对象都拥有类中定义的属性和方法的具体实例。
创建类的实例(对象)时,每个对象都会拥有独立的属性集,这意味着每个对象的属性可以有不同的值,但所有对象都会共享相同的方法。
public class TestCar {
public static void main(String[] args) {
// 创建 Car 类的对象
Car myCar = new Car();
// 访问属性并赋值
myCar.color = "Red";
myCar.year = 2021;
// 调用方法
myCar.accelerate();
}
}
构造方法:
构造方法是一种特殊类型的方法,在 Java 中用于初始化新创建的对象。构造方法的主要特点是它与类同名并且没有返回类型,不仅仅是没有 void
,而是连返回类型声明都没有。
//默认构造方法
public Student(){
System.out.println("默认构造方法");
}
//含参数的构造方法
public Student(String name, int age){
this.name = name;
this.age = age;
}
在 Java 中,如果一个类没有显式定义任何构造方法,编译器会为这个类提供一个默认的无参数构造方法。但如果你定义了至少一个构造方法(无论是有参数还是无参数的),编译器就不会提供默认的构造方法。因此,如果你想让你的类在不提供任何特定参数的情况下也能创建对象,你需要显式定义一个无参数的构造方法。
标准的JavaBean类:
package day2;
public class Student {
//private私有属性,表示只在这个类里面可以调用
//set方法:给成员变量赋值
//get方法:对外提供成员变量的值
//this:表示当前方法调用者的地址
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
private int age;
public void setAge(int age){
if(age >= 6 ) this.age = age;
}
public int getAge(){
return this.age;
}
//构造方法的名字应该和对象名字相同
//构造方法没有返回值,所以不需要写void这些返回类型。
//当不存在构造方法是,系统会自动生成默认构造方法,
// 一旦我们手写了构造方法,系统也不会生成默认方构造方法了。
//默认构造方法
public Student(){
System.out.println("默认构造方法");
}
//含参数的构造方法
public Student(String name, int age){
this.name = name;
this.age = age;
}
//其他行为。
}
在主方法中创建对象:
package day2;
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("李四");
s1.setAge(20);
//使用有参数的构造方法,就不需要调用set方法了
Student s2 = new Student("张三",24);
}
}
静态:
使用 static
关键字声明的成分属于类本身,而不是类的任何特定对象的实例。这意味着你可以在创建类的任何对象之前访问它们。
静态变量
静态变量(也称为类变量)是被类的所有实例共享的变量。无论你创建多少对象,静态变量只有一份拷贝。静态变量通常用于定义类级别的常量或者跟踪类的所有实例共享的信息。
public class Student {
static int studentCount = 0; // 静态变量,用于计数
// 构造方法
public Student() {
studentCount++; // 每次创建对象时增加计数
}
// 静态方法,获取当前学生计数
public static int getStudentCount() {
return studentCount;
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
System.out.println("Total students: " + Student.getStudentCount()); // 输出学生总数
}
}
在这个例子中,studentCount
是一个静态变量,用于跟踪创建的 Student
对象的数量。无论创建多少个 Student
对象,都只有一个 studentCount
变量。
静态方法
静态方法是属于类的方法,而不是类的实例。这意味着你可以不创建类的实例而调用静态方法。静态方法只能直接访问类的静态成员(变量和方法),不能直接访问非静态成员。
public class Calculator {
public static int add(int a, int b) {
return a + b; // 静态方法,可以通过类名直接调用
}
}
在这个例子中,add
是一个静态方法,可以使用 Calculator.add(5, 3)
来调用,而不需要创建 Calculator
类的对象。
package Static;
//工具类,不需要创建对象,而是运用其中的方法
public class ArrayUtil {
//私有化构造方法,目的是为了不让外界创建他的对象
private ArrayUtil() {}
//需要定义为静态的,方便调用
// 静态方法中没有this关键字
// 静态里面不能调用非静态的,非静态可以访问所有
public static String PrintArray(int[] arr) {
//System.out.println(showArray(arr));无法访问
StringBuffer sb = new StringBuffer();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) sb.append(arr[i]);
else sb.append(arr[i] + ",");
}
sb.append("]");
return sb.toString();
}
public int showArray(int[] arr) {
return arr.length;
}
}
主方法当中的调用:
package Static;
public class TestArray {
public static void main(String[] args) {
//测试工具类中的方法是否正确
int arr[] = {1,2,3,4,5};
String str = ArrayUtil.PrintArray(arr);
System.out.println(str);
}
}
继承:
基本概念:
超类(父类)和子类:
超类(父类):被其他类继承的类称为超类(父类)。
子类:继承另一个类的类称为子类。子类继承父类的公共和受保护的属性和方法。
extends 关键字:
在 Java 中,类通过使用 extends
关键字继承另一个类。例如,public class Dog extends Animal
表示 Dog
类继承了 Animal
类。
方法重写(Overriding):
子类可以定义与父类中一模一样的方法(即相同的方法名、返回类型及参数列表)。这称为方法重写或覆盖。使用 @Override
注解可以帮助确保正确重写了父类方法。
代码示例:
package Heritage;
public class TestCharacter {
public static void main(String[] args) {
Zi z = new Zi();
z.ziShow();
}
}
class Fu{
String name = "Fu";
public void eat(){
System.out.println("吃饭菜");
}
}
class Zi extends Fu{
String name = "Zi";
public void ziShow(){
System.out.println(name);//就近原则
System.out.println(this.name);
System.out.println(super.name);
}
//应用场景:当父类中的方法不能满足子类现在的需求时,我们就需要把这个方法重写
//注意:子类中重写方方法上面需要加上@Override
//只有添加到虚方法表的方法才能重写,重写的方法和父类一致
@Override
public void eat(){
System.out.println("吃汉堡");
}
}
super 关键字:
子类可以通过 super
关键字引用其父类的属性和方法。这在方法重写时特别有用,可以通过 super
调用父类中被重写的方法。
代码示例
父类:
package Heritage;
//继承Object类
public class Animals {
private String name;
private int age;
public Animals(String name, int age) {
this.name = name;
this.age = age;
}
public Animals() {
}
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;
}
//如果用private修饰,子类就无法访问了,只能在本类中使用
//子类只能访问父类中非私有的成员
//行为
public void Eat(String name) {
System.out.println(name + " is eating");
}
public void Drink(String name) {
System.out.println(name + " is Drinking");
}
}
子类:
package Heritage;
public class Cat extends Animals{
public Cat() {}
//有参构造
public Cat(String name, int age) {
super(name,age);//调用父类的有参构造
}
public void ArrestMouse(String name){
System.out.println(name + "Arrest Mouse");
}
}
子类能继承父类的什么呢?
子类不能继承父类的构造方法,但是会默认的访问父类的无参构造,再执行自己
但是可以通过super调用,如果要调用父类的有参构造就需要在子类有参构造中的super里面添加参数
子类就可以继承父类的成员变量,但是private修饰的成员变量子类不能直接调用,成员方法能添加到虚方法表那么能继承否则不能。
不能添加到方法表中的:private,static,final修饰的。
继承与接口实现:
类可以通过实现接口来继承接口的方法。这与继承类不同,因为接口仅定义方法,不实现方法(除了默认方法和静态方法)。
多重继承的限制:
Java 不支持从多个类直接继承,以避免多重继承可能引发的问题(但c++允许)。不过,一个类可以实现多个接口,这在某种程度上提供了多重继承的功能。
多态:
多态允许你用一个统一的接口来引用多种不同的对象,而这些对象可以执行同一个动作但表现出不同的行为。
想象一下,如果有一个遥控器(这就是我们说的统一接口),你可以用它来控制不同的电器,比如电视、空调和音响。虽然“打开”这个按钮总是做同样的事情——发送一个开启信号——但接收到这个信号的设备会根据它的功能以不同的方式响应(电视显示画面,空调开始吹风,音响播放音乐)。这就是多态的实际应用:同一个操作,不同的执行结果。
多态需要满足的条件: 有继承/实现关系,有父类引用指向子类对象,有方法的重写。
class Animal {
void makeSound() {
System.out.println("Some generic sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow");
}
}
public class TestPolymorphism {
public static void main(String[] args) {
Animal myAnimal = new Dog(); // 使用 Animal 类型引用 Dog 对象
myAnimal.makeSound(); // 输出: Bark
myAnimal = new Cat(); // 同样的 Animal 类型引用现在指向 Cat 对象
myAnimal.makeSound(); // 输出: Meow
}
}
抽象类和抽象方法:
未完待续。。。