当前位置: 首页 > news >正文

android的第一个app项目(java版)

一.学习java重要概念

        java的基本类型的语言方法和C语言很像,这都是我们要学的东西和学过的东西。那些基础东西,就不和大家讨论了,一起看一下java的一些知识架构。

1.封装

        封装是面向对象编程中的一个核心概念,它涉及到将数据和操作数据的方法结合在一起,形成一个整体,即类。在封装的过程中,类的内部实现对外部是隐藏的,只有通过定义好的接口才能与类的对象进行交互。这样做的目的是为了保护对象内部的数据不被外部随意访问和修改,确保数据的安全性和程序的健壮性。

        一句话:将方法和字段一起包装到一个单元中,单元以类的形式实现。 类比C语言:

struct student{//特征int age;char* name;double score;//行为void (*introduce)(int age,char* name,double score);void (*testFunc)();
}

         如何实现封装: 在Java中,封装通常是通过将类的字段(属性)设置为私有(private)来实现的,然后通过公共(public)的getter和setter方法来访问和修改这些字段。例如:

public class Person {private String name;private int age;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;}
}

        在上述代码中,nameage字段被设置为私有的,外部代码不能直接访问这些字段,而是必须通过getNamesetNamegetAgesetAge方法来进行访问和修改。 

         封装的实际应用: 在实际开发中,封装可以帮助我们隐藏实现细节,只暴露必要的操作接口,这样即使内部实现发生变化,也不会影响到使用该类的其他代码。例如,如果我们有一个BankAccount类,我们可以隐藏账户的余额,只提供存款和取款的方法:        

public class BankAccount {static double balance;public void deposit(double amount) {if (amount > 0) {balance += amount;}}public void withdraw(double amount) {if (amount > 0 && balance >= amount) {balance -= amount;}}public double getBalance() {return balance;}
}
public class getmoney {BankAccount stu1 = new BankAccount();stu1.balance=7;stu1.deposit(100);stu1.witrhdraw(99);stu1.getBalance();System.out.println("balance="+stu1.balance);
}

类-----模板

类不能直接使用,不能直接访问变量,需要先实例化,申请一个空间

2.封装的访问修饰符

封装标识符
位置private默认protectedpublic
同一个类
同一个包内的类
不同包内的子类
不同包并且不是子类

public:该类或非该类均可以访问

private:只有该类可以访问

protected:该类及其子类的成员可以访问,同一个包中的类可以访问

默认:同一个包中的类可以访问

pubilc class year{private int Year;private boolean isLeapYear(){if((year%4==0&&year%100!=0||year%400==0)){return true;}else{return false;}}
}

3.UML类图及构造方法

        统一建模语言(Unified Modeling Language,UML)是用来设计软件的可视化建模语言。它的特点是简单、统一、图形化、能表达软件设计中的动态与静态信息。

        UML 从目标系统的不同角度出发,定义了用例图、类图、对象图、状态图、活动图、时序图、协作图、构件图、部署图等 9 种图。

        属性/方法名称前加的加号和减号表示了这个属性/方法的可见性,UML类图中表示可见性的符号有三种:

  • +:表示public

  • -:表示private

  • #:表示protected

  • 属性的完整表示方式是: 可见性 名称 :类型 [ = 缺省值]

    方法的完整表示方式是: 可见性 名称(参数列表) [ : 返回类型]

 注意:构造方法的调用,特别是new操作符(实例化对象的时候,自动被调用)

规则:(1)构造方法名和类名一致(2)没有返回类型(3)方式实现主要为字段赋初值

public class People {private String name;private int age;//无参构造public People() {System.out.println("一个人的诞生");}//有参构造方法public People(String name, int age) {this.name = name;this.age = age;}public static void main(String[] args) {People people = new People();}
}

         构造方法和方法是类似的,他也可以重载。其重载的方法很简单,就是只要提供不一样的参数即可,编译器就会去通过不同的参数找到对应的构造方法

public class People {private String name;private int age;private String sex;//无参构造public People() {System.out.println("一个人的诞生");}//有参构造方法public People(String name, int age, String sex) {this.name = name;this.age = age;this.sex = sex;}public People(String name) {this.name = name;}public static void main(String[] args) {People people = new People("张三",18,"女");People people2 = new People("小帅",2);People people3 = new People("老王");//姓名null年龄0//System.out.println("姓名" + people.name + "年龄" + people.age);}
}

 4.this关键字

        在Java编程语言中,this关键字是一个非常重要的概念,它代表当前对象的引用。这个关键字可以在类的实例方法和构造器中使用,主要有三种用法:

用法一:引用当前类的属性或方法

        当类的构造器或方法中有与类的成员变量同名的局部变量时,可以使用this来明确指代成员变量。例如,在一个账户类Account中,构造器接收namebalancepwd作为参数,为了避免与成员变量混淆,可以使用this来引用成员变量:

public class Account {private String name;private double balance;private String pwd;public Account(String name, double balance, String pwd) {this.name = name;this.balance = balance;this.pwd = pwd;}
}

         在上述代码中,this.namethis.balancethis.pwd确保了构造器中的参数正确地初始化了对象的成员变量,而不是简单地赋值给了局部变量。

用法二:调用当前类的方法

        this关键字还可以用来调用另外一个方法,如果没有使用的话。例如: 

public class InvokeCurrentClassMethod {public InvokeCurrentClassMethod() {}void method1() {}void method2() {this.method1();}public static void main(String[] args) {(new InvokeCurrentClassMethod()).method1();}
}

用法三:返回当前类的实例

        this关键字还可以用于返回当前类的实例,这在链式编程中非常有用,可以使得方法调用可以连续进行。例如:

public class ChainExample {private int value;public ChainExample setValue(int value) {this.value = value;return this;  // 返回当前对象的引用,用于链式调用}public void display() {System.out.println("Value: " + this.value);}
}
public class Main {public static void main(String[] args) {ChainExample example = new ChainExample();example.setValue(10).display();  // 方法链式调用}
}

具体实例:当没有调用this关键字

public class WithoutThisStudent {String name;int age;WithoutThisStudent(String name, int age) {name = name;age = age;}void out() {System.out.println(name+" " + age);}public static void main(String[] args) {WithoutThisStudent s1 = new WithoutThisStudent("沉默王二", 18);WithoutThisStudent s2 = new WithoutThisStudent("沉默王三", 16);s1.out();s2.out();}
}

        从结果中可以看得出来,尽管创建对象的时候传递了参数,但实例变量并没有赋值。这是因为如果构造方法中没有使用 this 关键字的话,name 和 age 指向的并不是实例变量而是参数本身。

null 0
null 0

当调用了this关键字 

public class WithThisStudent {String name;int age;WithThisStudent(String name, int age) {this.name = name;this.age = age;}void out() {System.out.println(name+" " + age);}public static void main(String[] args) {WithThisStudent s1 = new WithThisStudent("沉默王二", 18);WithThisStudent s2 = new WithThisStudent("沉默王三", 16);s1.out();s2.out();}
}

         这次,实例变量有值了,在构造方法中,this.xxx 指向的就是实例变量,而不再是参数本身了。

沉默王二 18
沉默王三 16

调用当前类的构造方法

public class InvokeConstrutor {InvokeConstrutor() {System.out.println("hello");}InvokeConstrutor(int count) {this();System.out.println(count);}public static void main(String[] args) {InvokeConstrutor invokeConstrutor = new InvokeConstrutor(10);}
}

        在有参构造方法 InvokeConstrutor(int count) 中,使用了 this() 来调用无参构造方法 InvokeConstrutor()。 来看一下输出结果。这个this()调用当前类的构造方法,必须放在第一位。

hello
10

 5.static关键字

        虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。

5.1 静态变量

static变量也称为静态变量,静态变量和非静态变量的区别:

  • 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化

  • 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响

public class Student {String name;//非静态变量int age;//非静态变量static String school = "郑州大学";//静态变量public Student(String name, int age) {this.name = name;this.age = age;}public static void main(String[] args) {Student s1 = new Student("沉默王二", 18);Student s2 = new Student("沉默王三", 16);}
}

public class Counter {int count = 0;Counter() {count++;System.out.println(count);}public static void main(String args[]) {Counter c1 = new Counter();Counter c2 = new Counter();Counter c3 = new Counter();}
}

 IDEA 中点了一下运行按钮,程序跑了起来。

1
1
1

public class StaticCounter {static int count = 0;StaticCounter() {count++;System.out.println(count);}public static void main(String args[]) {StaticCounter c1 = new StaticCounter();StaticCounter c2 = new StaticCounter();StaticCounter c3 = new StaticCounter();}
}

“来看一下输出结果。”

1
2

         解释一下,由于静态变量只会获取一次内存空间,所以任何对象对它的修改都会得到保留,所以每创建一个对象,count 的值就会加 1,所以最终的结果是 3,明白了吧。

 5.2 静态方法

   非静态方法,当其他类调用非静态方法的时候,要new一下实例化。

public class StaticMethodStudent {String name;int age;static String school = "郑州大学";public StaticMethodStudent(String name, int age) {this.name = name;this.age = age;}static void change() {school = "河南大学";}void out() {System.out.println(name + " " + age + " " + school);}public static void main(String[] args) {StaticMethodStudent.change();StaticMethodStudent s1 = new StaticMethodStudent("沉默王二", 18);StaticMethodStudent s2 = new StaticMethodStudent("沉默王三", 16);s1.out();s2.out();}
}

“来看一下程序的输出结果吧。

沉默王二 18 河南大学
沉默王三 16 河南大学

 5.3 静态块区 

静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。
在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。

public class StaticBlock {static {System.out.println("静态代码块");}public static void main(String[] args) {System.out.println("main 方法");}
}

构造方法用于对象的初始化。

静态初始化块,用于类的初始化操作。

               在静态初始化块中不能直接访问非staic成员。

“来看一下程序的输出结果吧。”

静态代码块
main 方法

                 静态代码块通常用来初始化一些静态变量,它会优先于 main() 方法执行。

public class StaticBlockNoMain {static {System.out.println("静态代码块,没有 main");}
}

“在命令行中执行 java StaticBlockNoMain 的时候,会抛出 NoClassDefFoundError 的错误。”

6.super关键字

“super 关键字的用法主要有三种。”

  • 指向父类对象;
  • 调用父类的方法;
  • super() 可以调用父类的构造方法。

6.1 指向父类对象

1.如果父类和子类拥有同样名称的字段,super 关键字可以用来访问父类的同名字段。 

public class ReferParentField {public static void main(String[] args) {new Dog().printColor();}
}class Animal {String color = "白色";
}class Dog extends Animal {String color = "黑色";void printColor() {System.out.println(color);System.out.println(super.color);}
}

 “父类 Animal 中有一个名为 color 的字段,子类 Dog 中也有一个名为 color 的字段,子类的 printColor() 方法中,通过 super 关键字可以访问父类的 color。”

“来看一下输出结果。”

黑色
白色

6.2 指向父类构造方法

2.父类 Animal 和子类 Dog ,在子类Dog中,可以创建一个构造方法,在构造方法中调用父类的构造方法,但是super()。必须放在首行。

public class ReferParentConstructor {public static void main(String[] args) {new Dog();}
}class Animal {Animal(){System.out.println("动物来了");}
}class Dog extends Animal {Dog() {super();System.out.println("狗狗来了");}
}

来看一下输出结果。

动物来了
狗狗来了

        在默认情况下,super() 是可以省略的,编译器会主动去调用父类的构造方法。也就是说,子类即使不使用 super() 主动调用父类的构造方法,父类的构造方法仍然会先执行。  

public class ReferParentConstructor {public static void main(String[] args) {new Dog();}
}class Animal {Animal(){System.out.println("动物来了");}
}class Dog extends Animal {Dog() {System.out.println("狗狗来了");}
}

“输出结果和之前一样。”

动物来了
狗狗来了

6.3 指向父类有参构造方法 

 3.super() 也可以用来调用父类的有参构造方法,这样可以提高代码的可重用性

class Person {int id;String name;Person(int id, String name) {this.id = id;this.name = name;}
}class Emp extends Person {float salary;Emp(int id, String name, float salary) {super(id, name);this.salary = salary;}void display() {System.out.println(id + " " + name + " " + salary);}
}public class CallParentParamConstrutor {public static void main(String[] args) {new Emp(1, "沉默王二", 20000f).display();}
}

        “Emp 类继承了 Person 类,也就继承了 id 和 name 字段,当在 Emp 中新增了 salary 字段后,构造方法中就可以使用 super(id, name) 来调用父类的有参构造方法。”

“来看一下输出结果。”

1 沉默王二 20000.0

7.final关键字

7.1 final 变量

1.被 final 修饰的变量无法重新赋值。换句话说,final 变量一旦初始化,就无法更改。

final int age = 18;

public class Pig {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

        我们创建一个测试类,并声明一个 final 修饰的 Pig 对象。如果尝试将 pig 重新赋值的话,编译器同样会生气。

final Pig pig = new Pig();

但我们仍然可以去修改 pig 对象的 name。 

final Pig pig = new Pig();
pig.setName("特立独行");
System.out.println(pig.getName()); // 特立独行

 另外,final 修饰的成员变量必须有一个默认值,否则编译器将会提醒没有初始化。

 final 和 static 一起修饰的成员变量叫做常量,常量名必须全部大写。

public class Pig {private final int age = 1;public static final double PRICE = 36.5;
}

 有时候,我们还会用 final 关键字来修饰参数,它意味着参数在方法体内不能被再修改。如果尝试去修改它的话,编译器会提示以下错误。

public class ArgFinalTest {public void arg(final int age) {}public void arg1(final String name) {}
}

 7.2 final 方法

         被 final 修饰的方法不能被重写。如果我们在设计一个类的时候,认为某些方法不应该被重写,就应该把它设计成 final 的。

public class Actor {public final void show() {}
}

 当我们想要重写该方法的话,就会出现编译错误。

 7.3 final 类

任何尝试从 final 类继承的行为将会引发编译错误。

public final class Writer {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}

 尝试去继承它,编译器会提示以下错误,Writer 类是 final 的,无法继承。

8.继承

        继承就是子类继承父类的属性和方法,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的方法

        我们来举个例子:动物有很多种,是一个比较大的概念。在动物的种类中,我们熟悉的有猫(Cat)、狗(Dog)等动物,它们都有动物的一般特征(比如能够吃东西,能够发出声音),不过又在细节上有区别(不同动物的吃的不同,叫声不一样)。

        在 Java 语言中实现 Cat 和 Dog 等类的时候,就需要继承 Animal 这个类。继承之后 Cat、Dog 等具体动物类就是子类,Animal 类就是父类。

        如果仅仅只有两三个类,每个类的属性和方法很有限的情况下确实没必要实现继承,但事情并非如此,事实上一个系统中往往有很多个类并且有着很多相似之处,比如猫和狗同属动物,或者学生和老师同属人。各个类可能又有很多个相同的属性和方法,这样的话如果每个类都重新写不仅代码显得很乱,代码工作量也很大。

        这时继承的优势就出来了:可以直接使用父类的属性和方法,自己也可以有自己新的属性和方法满足拓展,父类的方法如果自己有需求更改也可以重写。这样使用继承不仅大大的减少了代码量,也使得代码结构更加清晰可见

 所以这样从代码的层面上来看我们设计这个完整的 Animal 类是这样的:

class Animal
{public int id;public String name;public int age;public int weight;//构造方法来初始化对象public Animal(int id, String name, int age, int weight) {this.id = id;this.name = name;this.age = age;this.weight = weight;}//这里省略get set方法public void sayHello(){System.out.println("hello");}public void eat(){System.out.println("I'm eating");}public void sing(){System.out.println("sing");}
}

 而 Dog,Cat,Chicken 类可以这样设计:

class Dog extends Animal//继承animal
{public Dog(int id, String name, int age, int weight) {super(id, name, age, weight);//调用父类构造方法}
}
class Cat extends Animal{public Cat(int id, String name, int age, int weight) {super(id, name, age, weight);//调用父类构造方法}
}
class Chicken extends Animal{public Chicken(int id, String name, int age, int weight) {super(id, name, age, weight);//调用父类构造方法}//鸡下蛋public void layEggs(){System.out.println("我是老母鸡下蛋啦,咯哒咯!咯哒咯!");}
}
public class CallParentParamConstrutor {public static void main(String[] args) {new Chicken(1,"蔡徐坤", 20000,18000).layEggs();}
}

         各自的类继承 Animal 后可以直接使用 Animal 类的属性和方法而不需要重复编写,各个类如果有自己的方法也可很容易地拓展。

注意:父类的private属性,会被继承并且初始化在子类父对象中,只不过对外不可见。

        子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访到的。 

 9.方法重载

第一,改变参数的数目。

public class OverloadingByParamNum {public static void main(String[] args) {System.out.println(Adder.add(10, 19));System.out.println(Adder.add(10, 19, 20));}
}class Adder {static int add(int a, int b) {return a + b;}static int add(int a, int b, int c) {return a + b + c;}
}

第二,通过改变参数类型,也可以达到方法重载的目的。 

public class OverloadingByParamType {public static void main(String[] args) {System.out.println(Adder.add(10, 19));System.out.println(Adder.add(10.1, 19.2));}
}class Adder {static int add(int a, int b) {return a + b;}static double add(double a, double b) {return a + b;}
}

 第三.改变参数的数目和类型都可以实现方法重载,为什么改变方法的返回值类型就不可以呢?

         编译时报错优于运行时报错,所以当两个方法的名字相同,参数个数和类型也相同的时候,虽然返回值类型不同,但依然会提示方法已经被定义的错误。

         注意:方法的返回值只是作为方法运行后的一个状态,它是保持方法的调用者和被调用者进行通信的一个纽带,但并不能作为某个方法的‘标识’。

第四.Java 虚拟机在运行的时候只会调用带有 String 数组的那个 main() 方法。

public class OverloadingMain {public static void main(String[] args) {System.out.println("String[] args");}public static void main(String args) {System.out.println("String args");}public static void main() {System.out.println("无参");}
}

 第一个 main() 方法的参数形式为 String[] args,是最标准的写法;第二个 main() 方法的参数形式为 String args,少了中括号;第三个 main() 方法没有参数。

String[] args

第五.由于可以通过改变参数类型的方式实现方法重载,那么当传递的参数没有找到匹配的方法时,就会发生隐式的类型转换。 

public class OverloadingTypePromotion {void sum(int a, long b) {System.out.println(a + b);}void sum(int a, int b, int c) {System.out.println(a + b + c);}public static void main(String args[]) {OverloadingTypePromotion obj = new OverloadingTypePromotion();obj.sum(20, 20);obj.sum(20, 20, 20);}
}

        “执行 obj.sum(20, 20) 的时候,发现没有 sum(int a, int b) 的方法,所以此时第二个 20 向上转型为 long,所以调用的是 sum(int a, long b) 的方法。”

“再来看一个示例。”

public class OverloadingTypePromotion1 {void sum(int a, int b) {System.out.println("int");}void sum(long a, long b) {System.out.println("long");}public static void main(String args[]) {OverloadingTypePromotion1 obj = new OverloadingTypePromotion1();obj.sum(20, 20);}
}

        执行 obj.sum(20, 20) 的时候,发现有 sum(int a, int b) 的方法,所以就不会向上转型为 long。来看一下程序的输出结果。

int

        当有两个方法 sum(long a, int b) 和 sum(int a, long b),参数个数相同,参数类型相同,只不过位置不同的时候,会发生什么呢? 

        不明确,编译器会很为难,究竟是把第一个 20 从 int 转成 long 呢,还是把第二个 20 从 int 转成 long,智障了!所以,不能写这样让编译器左右为难的代码。 

 10.方法重写

  • 重写的方法必须和父类中的方法有着相同的名字;
  • 重写的方法必须和父类中的方法有着相同的参数;
  • 必须是 is-a 的关系(继承关系)。
public class Bike extends Vehicle {@Overridevoid run() {System.out.println("自行车在跑");}public static void main(String[] args) {Bike bike = new Bike();bike.run();}
}class Vehicle {void run() {System.out.println("车辆在跑");}
}

         在方法重写的时候,IDEA 会建议使用 @Override 注解,显式的表示这是一个重写后的方法,尽管可以缺省。

 10.1规则一:只能重写继承过来的方法。

        因为重写是在子类重新实现从父类继承过来的方法时发生的,所以只能重写继承过来的方法,这很好理解。这就意味着,只能重写那些被 public、protected 或者 default 修饰的方法,private 修饰的方法无法被重写。

public class Animal {public void move() { }protected void eat() { }void sleep(){ }
}
//Dog 类来重写这三个方法:public class Dog extends Animal {public void move() { }protected void eat() { }void sleep(){ }
}

        但如果父类中的方法是 private 的,就行不通了。因为父类的 move() 方法是 private 的,对子类并不可见。

public class Dog extends Animal {public void move() { }
}

 10.2 规则二:final、static 的方法不能被重写。

class Animal {public final void eat() {System.out.println("Animal is eating");}
}class Dog extends Animal {// 下面这行代码会报错,不能重写父类的 final 方法// public void eat() {//     System.out.println("Dog is eating");// }
}

一个方法是 final 的就意味着它无法被子类继承到,所以就没办法重写。 

public class Animal {final void move() { }
}

  static 方法是属于类本身的,而不是某个对象的实例,因此,它的行为与实例方法不同。

      虽然static 方法也可以在子类中定义一个同名的方法,但它并不会覆盖父类中的 static 方法,因此 静态方法不能被真正的重写。

class Animal {public static void eat() {System.out.println("Animal is eating");}
}class Dog extends Animal {public static void eat() {System.out.println("Dog is eating");}
}public class Test {public static void main(String[] args) {Animal.eat();  // 输出:Animal is eatingDog.eat();     // 输出:Dog is eating}
}

10.3 规则三:重写的方法必须有相同的参数列表。 

public class Animal {void eat(String food) { }
}

        Dog 类中的 eat() 方法保持了父类方法 eat() 的同一个调调,都有一个参数——String 类型的 food。

public class Dog extends Animal {public void eat(String food) { }
}

 10.4 规则四:重写的方法必须返回相同的类型。

父类没有返回类型: 

public class Animal {void eat(String food) { }
}

 子类尝试返回 String:

public class Dog extends Animal {public String eat(String food) {return null;}
}

于是就编译出错了(返回类型不兼容)。 

 10.5 规则五:重写的方法不能使用限制等级更严格的权限修饰符。

  • 如果被重写的方法是 default,那么重写的方法可以是 default、protected 或者 public。
  • 如果被重写的方法是 protected,那么重写的方法只能是 protected 或者 public。
  • 如果被重写的方法是 public, 那么重写的方法就只能是 public。

举个例子,父类中的方法是 protected:

public class Animal {protected void eat() { }
}

子类中的方法可以是 public: 

public class Dog extends Animal {public void eat() { }
}

 如果子类中的方法用了更严格的权限修饰符,编译器就报错了。

 10.6 规则六:重写后的方法不能抛出比父类中更高级别的异常。

        举例来说,如果父类中的方法抛出的是 IOException,那么子类中重写的方法不能抛出 Exception,可以是 IOException 的子类或者不抛出任何异常。这条规则只适用于可检查的异常。

        可检查(checked)异常必须在源代码中显式地进行捕获处理,不检查(unchecked)异常就是所谓的运行时异常,比如说 NullPointerException、ArrayIndexOutOfBoundsException 之类的,不会在编译器强制要求。

 父类抛出 IOException:

public class Animal {protected void eat() throws IOException { }
}

 子类抛出 FileNotFoundException 是可以满足重写的规则的,因为 FileNotFoundException 是 IOException 的子类。

public class Dog extends Animal {public void eat() throws FileNotFoundException { }
}

如果子类抛出了一个新的异常,并且是一个 checked 异常:

public class Dog extends Animal {public void eat() throws FileNotFoundException, InterruptedException { }
}

那编译器就会提示错误:

Error:(9, 16) java: com.itwanger.overriding.Dog中的eat()无法覆盖com.itwanger.overriding.Animal中的eat()被覆盖的方法未抛出java.lang.InterruptedException

 10.7 可以在子类中通过 super 关键字来调用父类中被重写的方法。

public class Animal {protected void eat() { }
}

子类重写了 eat() 方法,然后在子类的 eat() 方法中,可以在方法体的第一行通过 super.eat() 调用父类的方法,然后再增加属于自己的代码。

public class Dog extends Animal {public void eat() {super.eat();// Dog-eat}
}

 10.8 规则八:构造方法不能被重写。

        因为构造方法很特殊,而且子类的构造方法不能和父类的构造方法同名(类名不同),所以构造方法和重写之间没有任何关系。

 11.包

 Java 定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名

包可以是多层结构,用.隔开。例如:java.util

要特别注意:包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。

package_sample
└─ src├─ hong│  └─ Person.java│  ming│  └─ Person.java└─ mr└─ jun└─ Arrays.java

         编译后的.class文件也需要按照包结构存放。如果使用 IDE,把编译后的.class文件放到bin目录下,那么,编译的文件结构就是:

package_sample
└─ bin├─ hong│  └─ Person.class│  ming│  └─ Person.class└─ mr└─ jun└─ Arrays.class

        导入包,在一个class中,我们总会引用其他的class。例如,小明的ming.Person类,如果要引用小军的mr.jun.Arrays类,他有三种写法:

第一种,直接写出完整类名,例如: 

// Person.java
package ming;public class Person {public void run() {mr.jun.Arrays arrays = new mr.jun.Arrays();}
}

第二种写法是用import语句 ,例如:

// Person.java
package ming;// 导入完整类名:
import mr.jun.Arrays;public class Person {public void run() {Arrays arrays = new Arrays();}
}

还有一种import static的语法,它可以导入一个类的静态字段和静态方法: 

package main;// 导入System类的所有静态字段和静态方法:
import static java.lang.System.*;public class Main {public static void main(String[] args) {// 相当于调用System.out.println(…)out.println("Hello, world!");}
}

12.工厂模式的实现

        工厂模式将对象的创建过程封装在工厂类中,客户端代码不需要知道如何实例化对象的细节,客户端只需要请求工厂来获取对象即可。这意味着,工厂类负责对象的创建,客户端只关心如1何使用对象。

class Fruit {String name;// 构造方法public Fruit(String name) {this.name = name;}void grup() {System.out.println("水果野蛮生长");}
}class Apple extends Fruit {// 重写 grup 方法@Overridevoid grup() {System.out.println("苹果野蛮生长");}// 构造方法public Apple(String name) {super(name); // 调用父类的构造方法}
}class Xigua extends Fruit {// 重写 grup 方法@Overridevoid grup() {System.out.println("西瓜野蛮生长");}// 构造方法public Xigua(String name) {super(name); // 调用父类的构造方法}
}class Factory {public Fruit getFruit(String name) {if (name.equals("苹果")) {return new Apple("苹果16");} else if (name.equals("西瓜")) {return new Xigua("西瓜16");} else {return null;}}
}public class Test {public static void main(String[] args) {Factory factory = new Factory();// 使用字符串来调用 getFruit 方法factory.getFruit("苹果").grup();// 输出:苹果野蛮生长factory.getFruit("西瓜").grup();// 输出:西瓜野蛮生长}
}

13.抽象类

抽象类的特点:

        1.定义中含有抽象方法的类叫抽象类

        2.抽象类用abstract来修饰

        3.抽象类代表一种抽象的对象类型

        4.抽象类不能实例化

        5.抽象类中可以有具体方法,可以没有抽象方法

        定义抽象类的时候需要用到关键字 abstract,放在 class 关键字前,就像下面这样。 

abstract class AbstractPlayer {
}

         抽象类是不能实例化的,尝试通过 new 关键字实例化的话,编译器会报错,提示“类是抽象的,不能实例化”。

        虽然抽象类不能实例化,但可以有子类。子类通过 extends 关键字来继承抽象类。就像下面这样。 

public class BasketballPlayer extends AbstractPlayer {
}

如果一个类定义了一个或多个抽象方法,那么这个类必须是抽象类。 

抽象类中既可以定义抽象方法,也可以定义普通方法,就像下面这样:

public abstract class AbstractPlayer {abstract void play();public void sleep() {System.out.println("运动员也要休息而不是挑战极限");}
}

        抽象类派生的子类必须实现父类中定义的抽象方法。比如说,抽象类 AbstractPlayer 中定义了 play() 方法,子类 BasketballPlayer 中就必须实现。 

public class BasketballPlayer extends AbstractPlayer {@Overridevoid play() {System.out.println("我是张伯伦,篮球场上得过 100 分");}
}

 如果没有实现的话,编译器会提示“子类必须实现抽象方法”,见下图。

 13.1 抽象类应用模板方法模式

         这个方法调用了抽象方法 initUart()getcomand()opencurse()openlight()openTV()。但实际上,你并不需要在抽象类中提供这些方法的实现,抽象类的作用只是定义接口,而这些方法的具体实现由子类来完成。

abstract class DD {// 抽象方法abstract void initUart();abstract void getcomand();abstract void opencurse();abstract void openlight();abstract void openTV();// 抽象类中的工作方法void work() {initUart();getcomand();opencurse();openlight();openTV();}
}class STC15wCon extends DD {@Overridevoid initUart() {System.out.println("STC15wCon initUart");}@Overridevoid getcomand() {System.out.println("STC15wCon getcomand");}@Overridevoid opencurse() {System.out.println("STC15wCon opencurse");}@Overridevoid openlight() {System.out.println("STC15wCon openlight");}@Overridevoid openTV() {System.out.println("STC15wCon openTV");}
}public class Test {public static void main(String[] args) {STC15wCon c = new STC15wCon();  // 注意这里需要括号c.work();  // 调用work方法}
}

 输出结果:

        这样,work() 方法在 DD 抽象类中被定义,并且通过子类 STC15wCon 提供了各个具体方法的实现。当 c.work() 被调用时,所有的抽象方法都被执行。

STC15wCon initUart
STC15wCon getcomand
STC15wCon opencurse
STC15wCon openlight
STC15wCon openTV

14.接口

接口通过 interface 关键字来定义,它可以包含一些常量和抽象方法。

public interface Electronic {// 常量String LED = "LED";// 抽象方法int getElectricityUse();// 静态方法static boolean isEnergyEfficient(String electtronicType) {return electtronicType.equals(LED);}// 默认方法default void printDescription() {System.out.println("电子");}
}

1)接口不允许直接实例化否则编译器会报错。

public class Computer implements Electronic {public static void main(String[] args) {new Computer();}@Overridepublic int getElectricityUse() {return 0;}
}

 2)接口可以是空的,既可以不定义变量,也可以不定义方法。最典型的例子就是 Serializable 接口,在 Java.io包下。

public interface Serializable {
}

         Serializable 接口用来为序列化的具体实现提供一个标记,也就是说,只要某个类实现了 Serializable 接口,那么它就可以用来序列化了。

3)不要在定义接口的时候使用 final 关键字否则会报编译错误,因为接口就是为了让子类实现的,而 final 阻止了这种行为。 

 4)接口的抽象方法不能是 private、protected 或者 final,否则编译器都会报错。

        在接口中,所有的 方法默认就是抽象的,因此你不需要显式地使用 abstract 来声明它们。接口中的方法默认是 public abstract,即使不写 abstract,编译器会隐式地将其视为抽象方法。

 5)接口的变量是隐式 public static final(常量),所以其值无法改变。

 15.object类

        在 Java 中,所有类都隐式地继承自 Object 类,除非显式声明继承自其他类。Object 是 Java 类层次结构中的根类,它提供了许多重要的方法,所有继承自 Object 的类都能使用这些方法。因为所有类都继承自 Object,你几乎总是会用到 Object 类的一些方法。

1.toString()

  • 该方法用于返回对象的字符串表示。默认实现返回的是对象的类名和哈希码,但通常我们会重写它以提供更有意义的输出。
class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person[name=" + name + ", age=" + age + "]";}
}public class Test {public static void main(String[] args) {Person p = new Person("Alice", 25);System.out.println(p.toString()); // 输出:Person[name=Alice, age=25]}
}

2.equals(Object obj)

  • equals() 用于比较两个对象是否相等。默认实现比较的是对象的引用是否相同(即它们是否指向相同的内存地址)。为了使其按内容比较(比如比较字符串内容或对象的属性),通常需要重写该方法。
class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object obj) {if (this == obj) {return true;}if (obj == null || getClass() != obj.getClass()) {return false;}Person person = (Person) obj;return age == person.age && name.equals(person.name);}
}public class Test {public static void main(String[] args) {Person p1 = new Person("Alice", 25);Person p2 = new Person("Alice", 25);System.out.println(p1.equals(p2)); // 输出:true}
}

 3.hashCode()

  • hashCode() 方法用于返回对象的哈希码,通常与 equals() 方法一起使用。两者应该遵循一个原则:如果两个对象通过 equals() 比较相等,它们的 hashCode() 必须相同。
  • 默认实现基于对象的内存地址,但通常我们会根据对象的属性来重写 hashCode() 方法。
@Override
public int hashCode() {return Objects.hash(name, age);
}

4.clone()

  • clone() 方法用于创建当前对象的副本。为了能使用此方法,类需要实现 Cloneable 接口,否则会抛出 CloneNotSupportedException 异常。
class Person implements Cloneable {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}public class Test {public static void main(String[] args) throws CloneNotSupportedException {Person p1 = new Person("Alice", 25);Person p2 = (Person) p1.clone();System.out.println(p1 == p2); // 输出:false,说明是不同的对象}
}

 5.getClass()

  • getClass() 返回当前对象的 Class 实例,代表该对象的运行时类。这对于反射操作非常有用。
class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}
}public class Test {public static void main(String[] args) {Person p = new Person("Alice", 25);System.out.println(p.getClass().getName()); // 输出:Person}
}

 6.具体综合实例

  • 在 Java 中,所有类都继承自 Object 类,即使没有显式声明继承,也会继承 Object 类的方法。
  • Object 类提供了很多通用的方法,如 toString()equals()hashCode()getClass()clone() 等,通常会根据实际需求重写这些方法。
  • 重写 equals()hashCode() 是非常常见的做法,尤其是在处理集合类时,比如使用 HashMapHashSet 等时,Java 会依赖这些方法来判断对象是否相等或进行对象存储。
class Person {String name;public Person(String name) {this.name = name;}@Overridepublic String toString() {return "Person[name=" + name + "]";}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;//强制转换为Person对象return name.equals(person.name);}@Overridepublic int hashCode() {return name.hashCode();}
}public class Test {public static void main(String[] args) {Person p1 = new Person("Alice");Person p2 = new Person("Alice");System.out.println(p1.toString());  // 输出:Person[name=Alice]System.out.println(p1.equals(p2));  // 输出:trueSystem.out.println(p1.hashCode());  // 输出:哈希码}
}

16.内部类

(1) 成员内部类

        成员内部类:创建成员内部类对象时需要先创建外部类的对象,因为成员内部类实例是与外部类实例相关联的。

成员内部类是最常见的内部类,看下面的代码:

class Wanger {int age = 18;class Wangxiaoer {int age = 81;}
}

成员内部类可以无限制访问外部类的所有成员属性。 

public class Wanger {int age = 18;private String name = "沉默王二";static double money = 1;class Wangxiaoer {int age = 81;public void print() {System.out.println(name);System.out.println(money);}}
}

      内部类可以随心所欲地访问外部类的成员,但外部类想要访问内部类的成员,就不那么容易了,必须先创建一个成员内部类的对象,再通过这个对象来访问: 

public class Wanger {int age = 18;private String name = "沉默王二";static double money = 1;public Wanger () {new Wangxiaoer().print();}class Wangxiaoer {int age = 81;public void print() {System.out.println(name);System.out.println(money);}}
}

         如果想要在静态方法中访问成员内部类的时候,就必须先得创建一个外部类的对象,因为内部类是依附于外部类的。

public class Wanger {int age = 18;private String name = "沉默王二";static double money = 1;public Wanger () {new Wangxiaoer().print();}public static void main(String[] args) {Wanger wanger = new Wanger();Wangxiaoer xiaoer = wanger.new Wangxiaoer();xiaoer.print();}class Wangxiaoer {int age = 81;public void print() {System.out.println(name);System.out.println(money);}}
}

外部类名.内部类名  实例 = 外部类实例名.new  内部类构造方法(参数)

(2) 局部内部类

        局部内部类:局部内部类通常在外部方法中创建实例,不需要创建外部类的实例(如果局部内部类是该方法中的一部分)。

public class OuterClass {public void outerMethod() {int num = 10;  // 方法中的局部变量final int factor = 2; // final 局部变量// 局部内部类class InnerClass {public void display() {System.out.println("num: " + num);  // 可以访问 numSystem.out.println("factor: " + factor);  // 可以访问 final 局部变量}}// 创建局部内部类的实例InnerClass inner = new InnerClass();inner.display();}
}public class Main {public static void main(String[] args) {OuterClass outer = new OuterClass();outer.outerMethod();  // 调用外部类方法,进而访问局部内部类}
}

注意事项:

  • 局部内部类只能在其所在的方法、构造函数或代码块中使用,不能在方法外部声明或使用。
  • 由于它们访问外部方法中的局部变量,这些变量必须是 final 或有效 final,即不能被重新赋值。

(3) 匿名内部类

        匿名内部类是唯一一种没有构造方法的类。就上面的写法来说,匿名内部类也不允许我们为其编写构造方法,因为它就像是直接通过 new 关键字创建出来的一个对象。

        匿名内部类的作用主要是用来继承其他类或者实现接口,并不需要增加额外的方法,方便对继承的方法进行实现或者重写。

public class ThreadDemo {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());}});t.start();}
}

 (4) 静态内部类

静态内部类和成员内部类类似,只是多了一个 static关键字。

由于 static 关键字的存在,静态内部类是不允许访问外部类中非 static 的变量和方法的。

public class Wangsi {static int age;double money;static class Wangxxiaosi {public Wangxxiaosi (){System.out.println(age);}}
}

 17.多态

 多态存在的三个必要条件:

(1)需要存在继承和实现关系

(2)同样的方法调用而执行不同操作,运行不同代码(重写)

(3)在运行时父类或者接口的引用变量可以引用其子类的对象多态的对象

abstract class Animal {public String name;// 抽象的 eat 方法,没有参数abstract public void eat();
}class Dog extends Animal {@Overridepublic void eat() {// 在这里使用类的 name 属性,假设 name 已经被赋值System.out.println(name + " is eating.");}// 如果想要使用一个方法设置 name,可以新定义一个方法public void setName(String name) {this.name = name;}
}
class cat extends Animal {@Overridepublic void eat() {// 在这里使用类的 name 属性,假设 name 已经被赋值System.out.println(name + " is eating.");}// 如果想要使用一个方法设置 name,可以新定义一个方法public void setName(String name) {this.name = name;}
}public class Main {public static void main(String[] args) {animal an1 = new Dog();animal an2 = new cat();an1.eat();an2.eat();}
}

18.多态中上下转型的关系--instanceof关键字

(object) instanceof (type)

        用意也非常简单,判断对象是否符合指定的类型,结果要么是 true,要么是 false。在反序列化的时候,instanceof 操作符还是蛮常用的,因为这时候我们不太确定对象属不属于指定的类型,如果不进行判断的话,就容易抛出 ClassCastException 异常。 

(1)新建一个接口 Shape: 

interface Shape {
}

 (2)然后新建 Circle 类实现 Shape 接口并继承 Round 类:

class Circle extends Round implements Shape {
}

 (3)如果对象是由该类创建的,那么 instanceof 的结果肯定为 true。

Circle circle = new Circle();
System.out.println(circle instanceof Circle);

 (4)这个肯定没毛病,instanceof 就是干这个活的,大家也很好理解。那如果类型是父类呢?

System.out.println(circle instanceof Round);

 (5)结果肯定还是 true,因为依然符合 is-a 的关系。那如果类型为接口呢?

System.out.println(circle instanceof Shape);

 (6)结果仍然为 true, 因为也符合 is-a 的关系。如果要比较的对象和要比较的类型之间没有关系,当然是不能使用 instanceof 进行比较的。

为了验证这一点,我们来创建一个实现了 Shape 但与 Circle 无关的 Triangle 类:

class Triangle implements Shape {
}

 这时候,再使用 instanceof 进行比较的话,编译器就报错了。

 System.out.println(circle instanceof Triangle);

 错误信息如下所示:

Inconvertible types; cannot cast 'com.itwanger.twentyfour.instanceof1.Circle' to 'com.itwanger.twentyfour.instanceof1.Triangle'

意思就是类型不匹配,不能转换,我们使用 instanceof 比较的目的,也就是希望如果结果为 true 的时候能进行类型转换。

 (7)强转类型

先用 instanceof 进行类型判断,然后再把 obj 强制转换成我们期望的类型再进行使用。

// 先判断类型
if (obj instanceof String) {// 然后强制转换String s = (String) obj;// 然后才能使用
}

 JDK 16 的时候,instanceof 模式匹配转了正,意味着使用 instanceof 的时候更便捷了。

if (obj instanceof String s) {// 如果类型匹配 直接使用 s
}

 19.异常

        “异常是指中断程序正常执行的一个不确定的事件。当异常发生时,程序的正常执行流程就会被打断。一般情况下,程序都会有很多条语句,如果没有异常处理机制,前面的语句一旦出现了异常,后面的语句就没办法继续执行了。”

 19.1 checked和unchecked异常

        checked 异常(检查型异常)在源代码里必须显式地捕获或者抛出,否则编译器会提示你进行相应的操作;而 unchecked 异常(非检查型异常)就是所谓的运行时异常,通常是可以通过编码进行规避的,并不需要显式地捕获或者抛出 

        unchecked 异常可以不在程序中显示处理,就像之前提到的 ArithmeticException 就是的;但 checked 异常必须显式处理。 

         首先,Exception 和 Error 都继承了 Throwable 类。换句话说,只有 Throwable 类(或者子类)的对象才能使用 throw 关键字抛出,或者作为 catch 的参数类型。

建议你要么使用 try-catch 进行捕获: 

try {Class clz = Class.forName("com.itwanger.s41.Demo1");
} catch (ClassNotFoundException e) {e.printStackTrace();
}

        注意打印异常堆栈信息的 printStackTrace() 方法,该方法会将异常的堆栈信息打印到标准的控制台下,如果是测试环境,这样的写法还 OK,如果是生产环境,这样的写法是不可取的,必须使用日志框架把异常的堆栈信息输出到日志系统中,否则可能没办法跟踪。

 要么在方法签名上使用 throws 关键字抛出:

public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {Class clz = Class.forName("com.itwanger.s41.Demo1");}
}

19.2 NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

  • NoClassDefFoundError:程序在编译时可以找到所依赖的类,但是在运行时找不到指定的类文件,导致抛出该错误;原因可能是 jar 包缺失或者调用了初始化失败的类。
  • ClassNotFoundException:当动态加载 Class 对象的时候找不到对应的类时抛出该异常;原因可能是要加载的类不存在或者类名写错了。

 19.3 try-catch-finally

(1)try{ }语句块中放的是要检测的java代码,可能有会抛出异常,也可能会正常执行。

(2)catch(异常类型){ }块是当java运行时系统接收到try块中所抛出异常对象中,会寻找能处理这一异常cathc块来进行处理(可以有多个cathc块)。

(3)finally{ }不管系统有没有抛出异常都会去执行,一般用来释放资源。除了在之前执行了System.exit(0)

 19.4 throw和throws

throw:用于手动抛出异常,作为程序员可以在任意位置手动抛出异常。

throws:用于在方法上标识要暴露的异常。抛出的异常交由调用者处理。

public class ThrowDemo {static void checkEligibilty(int stuage){if(stuage<18) {throw new ArithmeticException("年纪未满 18 岁,禁止观影");} else {System.out.println("请认真观影!!");}}public static void main(String args[]){checkEligibilty(10);System.out.println("愉快地周末..");}
}

这段代码在运行的时候就会抛出以下错误:

Exception in thread "main" java.lang.ArithmeticException: 年纪未满 18 岁,禁止观影at com.itwanger.s43.ThrowDemo.checkEligibilty(ThrowDemo.java:9)at com.itwanger.s43.ThrowDemo.main(ThrowDemo.java:16)

 20.泛型类

class ClassName<T> {private T variable;  // T 是泛型参数,可以是任意类型public ClassName(T variable) {this.variable = variable;}public T getVariable() {return variable;}public void setVariable(T variable) {this.variable = variable;}
}

在上面的代码中,T 是一个类型参数,它表示在使用 ClassName 时可以传入任何类型。

使用泛型类:

你可以在创建 ClassName 类的对象时,指定具体的类型:

public class Main {public static void main(String[] args) {// 创建泛型类的实例,类型为 StringClassName<String> stringInstance = new ClassName<>("Hello, Generics!");System.out.println(stringInstance.getVariable());  // 输出: Hello, Generics!// 创建泛型类的实例,类型为 IntegerClassName<Integer> integerInstance = new ClassName<>(100);System.out.println(integerInstance.getVariable());  // 输出: 100}
}

20.1 泛型类的多个类型参数

你可以在一个泛型类中使用多个类型参数,这样它就能够处理多个不同的类型。

class Pair<K, V> {private K key;private V value;public Pair(K key, V value) {this.key = key;this.value = value;}public K getKey() {return key;}public V getValue() {return value;}public void setKey(K key) {this.key = key;}public void setValue(V value) {this.value = value;}
}
public class Main {public static void main(String[] args) {// 创建一个键值对,键是 String,值是 IntegerPair<String, Integer> pair = new Pair<>("Age", 25);System.out.println(pair.getKey() + ": " + pair.getValue());  // 输出: Age: 25}
}

20.2 通配符(Wildcard)

泛型类的一个强大特性是你可以使用通配符来表示不关心的类型。例如,使用 ? 表示任意类型。

import java.util.ArrayList;
import java.util.List;public class Main {public static void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}}public static void main(String[] args) {List<String> stringList = new ArrayList<>();stringList.add("Hello");stringList.add("World");List<Integer> integerList = new ArrayList<>();integerList.add(1);integerList.add(2);printList(stringList);  // 输出:Hello WorldprintList(integerList);  // 输出:1 2}
}
  • list 是你传入的 List<?>,其中 ? 是通配符,表示这个 list 可以包含任何类型的对象。
  • for (Object obj : list) 的意思是:遍历 list 中的每个元素,将每个元素依次赋值给变量 obj,然后执行循环体中的代码。

        由于 list 的类型是 List<?>(一个泛型 List,但是具体的类型我们并不关心),obj 的类型是 Object。在 Java 中,所有类都是 Object 类的子类,因此 Object 可以接收 list 中的任何元素类型。

  • new ArrayList<>() 是在创建一个新的 ArrayList 实例,表示这是一个存储元素的可变大小的列表。
  • 尽管你没有显式地指定泛型类型,编译器能够根据你赋值给 List<String>List<Integer> 来推断出 ArrayList<String>ArrayList<Integer>

20.3 泛型继承

class Box<T> {private T value;public Box(T value) {this.value = value;}public T getValue() {return value;}
}class ColoredBox<T> extends Box<T> {private String color;public ColoredBox(T value, String color) {super(value);  // 调用父类的构造函数this.color = color;}public String getColor() {return color;}
}

使用泛型类的继承

public class Main {public static void main(String[] args) {ColoredBox<String> coloredBox = new ColoredBox<>("Hello", "Red");System.out.println(coloredBox.getValue());  // 输出: HelloSystem.out.println(coloredBox.getColor());  // 输出: Red}
}

20.4 泛型类型可以实现泛型接口

interface PairInterface<K, V> {K getKey();V getValue();
}class PairClass<K, V> implements PairInterface<K, V> {private K key;private V value;PairClass(K key, V value) {this.key = key;this.value = value;}@Overridepublic K getKey() {return key;}@Overridepublic V getValue() {return value;}
}public class Main {public static void main(String[] args) {PairClass<String, Integer> pair = new PairClass<>("One", 1);System.out.println(pair.getKey() + ": " + pair.getValue());  // 输出: One: 1}
}
  • 接口本身是泛型的:你可以定义一个泛型接口,接口中可以有类型参数。
  • 实现接口时,可以传递具体的类型:实现泛型接口的类也可以是泛型类,或者你可以指定具体的类型来实现接口。
  • 灵活性:泛型接口使得你能够定义一些与类型无关的行为,然后由实现该接口的类来具体化实现时的类型。

20.5 继承多个泛型参数及实现泛型接口 

// 泛型接口,接受一个泛型参数
interface Printable<T> {void print(T item);
}// 泛型类,接受两个泛型参数
class Storage<T, U> {private T item;  // 存储的物品private U metadata;  // 与物品相关的元数据public Storage(T item, U metadata) {this.item = item;this.metadata = metadata;}public T getItem() {return item;}public U getMetadata() {return metadata;}public void setItem(T item) {this.item = item;}public void setMetadata(U metadata) {this.metadata = metadata;}
}// 继承 Storage 并实现 Printable 的泛型类
class AdvancedStorage<T, U> extends Storage<T, U> implements Printable<T> {public AdvancedStorage(T item, U metadata) {super(item, metadata);}@Overridepublic void print(T item) {System.out.println("Printing item: " + item);}// 自定义方法,显示存储的物品和元数据public void showItemAndMetadata() {System.out.println("Stored item: " + getItem() + ", Metadata: " + getMetadata());}
}public class Main {public static void main(String[] args) {// 创建一个 AdvancedStorage 实例,存储 String 类型的数据和 Integer 类型的元数据AdvancedStorage<String, Integer> stringStorage = new AdvancedStorage<>("Hello, World!", 2025);stringStorage.showItemAndMetadata();  // 输出:Stored item: Hello, World!, Metadata: 2025stringStorage.print(stringStorage.getItem());  // 输出:Printing item: Hello, World!// 创建一个 AdvancedStorage 实例,存储 Integer 类型的数据和 Double 类型的元数据AdvancedStorage<Integer, Double> integerStorage = new AdvancedStorage<>(123, 45.67);integerStorage.showItemAndMetadata();  // 输出:Stored item: 123, Metadata: 45.67integerStorage.print(integerStorage.getItem());  // 输出:Printing item: 123}
}

   Main:在 main() 方法中,我们创建了 AdvancedStorage 的两个实例,一个存储 String 类型的数据和 Integer 类型的元数据,另一个存储 Integer 类型的数据和 Double 类型的元数据。然后,我们调用了 showItemAndMetadata()print() 方法来演示如何处理和打印这些数据。

21.限制泛型类型

  • 限制为某个类的子类:使用 extends 来限制泛型类型为某个类或其子类。
  • 限制为实现某个接口:使用 extends 来限制泛型类型为实现某个接口的类型。

21.1 限制泛型类型为某个类的子类或实现接口

// 限制泛型类型为 Number 或其子类
class NumberBox<T extends Number> {private T value;public NumberBox(T value) {this.value = value;}public T getValue() {return value;}public void printValue() {System.out.println("Value: " + value);}
}public class Main {public static void main(String[] args) {NumberBox<Integer> integerBox = new NumberBox<>(10);  // Integer 是 Number 的子类integerBox.printValue();  // 输出:Value: 10NumberBox<Double> doubleBox = new NumberBox<>(3.14);  // Double 是 Number 的子类doubleBox.printValue();  // 输出:Value: 3.14// 以下代码会报错,因为 String 不是 Number 的子类// NumberBox<String> stringBox = new NumberBox<>("Hello");}
}

        NumberBox<T extends Number>:T 必须是 Number 或其子类(如 Integer, Double 等)。你无法将 String 类型作为 T 传递给 NumberBox,因为 String 不是 Number 或其子类。

21.2 限制泛型为多个接口的实现

// 定义两个接口
interface Printable {void print();
}interface Showable {void show();
}// 泛型类,限制 T 必须实现 Printable 和 Showable 接口
class Display<T extends Printable & Showable> {private T item;public Display(T item) {this.item = item;}public void displayItem() {item.print();  // 调用 print 方法item.show();   // 调用 show 方法}
}// 实现这两个接口的类
class Product implements Printable, Showable {private String name;public Product(String name) {this.name = name;}@Overridepublic void print() {System.out.println("Product name: " + name);}@Overridepublic void show() {System.out.println("Showing product: " + name);}
}public class Main {public static void main(String[] args) {Product product = new Product("Laptop");Display<Product> display = new Display<>(product);display.displayItem();}
}

         T extends Printable & Showable:T 必须同时实现 Printable 和 Showable 接口。在 Display<T> 中,T 必须有 print 和 show 方法。

 21.3 使用通配符(Wildcard)进行限制

// 使用上界通配符
public class UpperBoundExample {public static void printNumbers(List<? extends Number> list) {for (Number number : list) {System.out.println(number);}}public static void main(String[] args) {List<Integer> integers = new ArrayList<>();integers.add(10);integers.add(20);List<Double> doubles = new ArrayList<>();doubles.add(3.14);doubles.add(2.71);printNumbers(integers);  // 输出:10 20printNumbers(doubles);   // 输出:3.14 2.71}
}

 通配符,上下转型中的应用。

class animal{}
class dog extends animal{}
class Cls1<T>{T a;public Cls1(T a){this.a = a;}public T getData(){return a;}
}
public class test{public static void main(String[] args){Cls1<? extends animal>c2;Cls1<? super dog>c3;Cls1<dog> c1=new Cls1<dog>(new dog());c2=c1;c3=c1;}}

21.4 泛型方法

访问修饰符 <泛型列表> 返回类型 方法名(参数列表){

        实现代码

}

  21.4.1 多泛型参数的泛型方法
public class MultiGenericMethod {// 定义一个多泛型参数的方法public static <T, U> void printPair(T first, U second) {System.out.println("First: " + first);System.out.println("Second: " + second);}public static void main(String[] args) {printPair(10, "Hello");   // 输出:First: 10, Second: HelloprintPair(3.14, true);    // 输出:First: 3.14, Second: true}
}
 21.4.2 通配符和泛型方法结合使用
public class WildcardGenericMethod {// 使用通配符上界来限制泛型类型public static void printNumbers(List<? extends Number> list) {for (Number number : list) {System.out.println(number);}}public static void main(String[] args) {List<Integer> integers = List.of(1, 2, 3);List<Double> doubles = List.of(3.14, 2.71);printNumbers(integers);  // 输出:1 2 3printNumbers(doubles);   // 输出:3.14 2.71}
}

 printNumbers(List<? extends Number> list):

  • 这个方法接受一个 List,其元素类型是 Number 或其子类。
  • ?extends Number 表示 List 中的元素类型可以是 Number 或 Number 的任何子类(如 Integer, Double)。
  • 通过通配符,方法可以接收多种类型的 List。

 二.Android项目正式开始

1.双击运行

 2.选择路径

3.结束

4.准备运行环境

        如果你电脑已有jdk环境,高于11以上即可,若版本太低可能会导致AndroidStudio一直初始化失败,建议下我所提供的jdk版本,电脑可以共存多个jdk,如若已经其他jdk配置环境变量,即可跳过此步骤

(1)环境配置

个人建议将jdk17工具包解压放在C盘根目录下,如下图

 (2)环境变量配置

        若你跟我一样默认放在C盘,那么位置就是C:\jdk-17.0.0.1

        接下来开始配置,找到系统环境设置(win11若你经常配置环境变量,应该是知道在哪里配置,设置→系统→系统信息→高级系统设置→高级→环境变量),不会的请自己百度

        主要是配置下面的系统变量

 你先把这两句复制到剪切板

JAVA_HOME%JAVA_HOME%\bin

开始配置:

 

 点击确定保存,然后往下找到变量为Path的,双击打开

点击新建,将%JAVA_HOME%\bin填入,然后将其移动到最上方即可,防止其他环境冲突 

        记住一定要点击确认,全部确认,此时你就已经完成了jdk17的环境配置,但是需要检验是否配置成功,在cmd命令窗口中输入 

 java --version

        若不是如下图,那你就只有自己检查是否哪里配置错误,当然你配置的其他版本jdk,那么版本肯定是不同的,我所演示是jdk17

 (3)SDK环境安装配置

        个人建议单独放在其他盘,专门建立个文件夹存放sdk 现在双击打开AndroidStudio运行,有些可能桌面安装没有快捷方式,你只需要按win键,找到所有英语,A字母开头的地方就能找到(以下操作请保持网络环境较好情况,防止出错)

 1.打开记得点击don’t send

 2.第一次会碰见以下,直接先取消

3. 然后点击Next,然后记得把这个sdk更换到其他盘,我就放到了E盘,建立了个文件夹

 

 

4. 然后点击Next,出现以下界面继续NextAccept,在点击AcceptF,然后Finish,等待下载即可

 

 5.直到出现以下Finish的方框变蓝可以点击的时候证明完成了,点击即可

(4)关键的地方 

到这个地方先叉掉软件,我们先继续配置环境,同样先将以下代码复制保存在剪切板中

 ANDROID_HOME​%ANDROID_HOME%\tools​%ANDROID_HOME%\platform-tools 

打开最初的环境变量配置设置(和配置JDK17一样的办法),点击环境变量新建,如下图

 

点击确认保存,再点击Path,添加以下两个 

 %ANDROID_HOME%\tools​%ANDROID_HOME%\platform-tools

 

此时就完成了SDK的配置 

(5)Gradle依赖库配置 

        首先你得先建立一个名为GradleRepository的文件夹(自定义目录),一 会儿进行配置,以下截图是我的路径,仅作为参考.复制以下文本,一会儿环境配置所用👇

 GRADLE_USER_HOME

一样打开你的环境变量配置,按照之前操作,新建环境变量 

 

点击确定(不点击确定可能会保存失败,两次确认关闭),然后!强烈建议重启你的电脑,强烈建议!!! 

 5.运行软件,进行最后的配置

1.检查相应设置是否调整准确

 

 然后点击OK

 2.进行Gradle下载和GradleRepository依赖库下载

 

 

 直到出现以下界面

以下操作个人强烈建议先断网,不然要操作几次,才能配置成功,也可以不断网,但保守嘛 

将下载源更改为腾讯镜像源

distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip

切记,只需要把services.gradle.org/distributions替换成mirrors.cloud.tencent.com/gradle就行了,其余地方不要动!!

 

然后在找默认依赖库下载源,只需要第一次更改,之后就不需要了,只下载一次,如若不更改,可能会下载很慢,长达一个小时,如若更改为国内源,几分钟的事情 找到settings.gradle.kts 

 

 将上图都替换成以下代码(这是2025年2月6日最新更新的源)

pluginManagement {repositories {maven { setUrl("https://mirrors.cloud.tencent.com/nexus/repository/maven-public/") }maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/central")}maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/google")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/jcenter")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/gradle-plugin")}google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}mavenCentral()gradlePluginPortal()}
}
dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {maven { setUrl("https://mirrors.cloud.tencent.com/nexus/repository/maven-public/") }maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/central")}maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/google")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/jcenter")}maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/gradle-plugin")}google()mavenCentral()}
}rootProject.name = "TestApplication"
include(":app")distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip

最好是Ctrl+S一下下吧,保守以下 此时你点击左上角的那个Try Again,记得联网,等待下载完成

三.Android如何导入其他项目

        一般下载下来的项目都有这些文件夹,在导入项目之前,首先删除gradle文件夹和下面红框的几个文件夹,其次打开整个项目的build.gradle文件,可以直接用记事本打开,就是下图中红圈里面的

 打开之后找到下图中的这一块,然后修改后面的版本号,我图里面的是2.2.2,自己可以看一下你平时项目的这个文件使用的版本号,然后进行修改。

修改完成之后,才可开始导入,但是导入的时候会弹出一个窗口。 

是否使用gradle wrapper,必须取消,就是每次导入其他人的项目特别慢的根源所在

取消之后会让你手动选择gradle版本,一般是在Android studio 的安装目录的gradle文件夹下面

例如:C:\Program Files\android\Android Studio\gradle

这个文件夹下面有当前可用的gradle版本,选择一个类似gradle-xxx的文件夹。然后确认,导入即可完成。

相关文章:

android的第一个app项目(java版)

一.学习java重要概念 java的基本类型的语言方法和C语言很像&#xff0c;这都是我们要学的东西和学过的东西。那些基础东西&#xff0c;就不和大家讨论了&#xff0c;一起看一下java的一些知识架构。 1.封装 封装是面向对象编程中的一个核心概念&#xff0c;它涉及到将数据和操…...

第6章 6.2使用ASP.NET Core 开发WebAPI ASP.NET Core Web API

6.2.1 Web API项目的搭建 进入VS&#xff0c;【创建新项目】&#xff0c;选择【ASP.NET Core Web API】模板&#xff0c;【下一步】&#xff0c;编辑项目名称及项目位置&#xff0c;【下一步】&#xff0c;选择框架&#xff0c;其他选项默认即可&#xff0c;【创建】。 进入项…...

常见的网络协议汇总(涵盖了不同的网络层次)

网络层协议 IP协议&#xff1a;IP指网际互连协议&#xff08;Internet Protocol&#xff09;&#xff0c;是TCP/IP体系中的网络层协议。IP协议包括IPv4和IPv6&#xff0c;用于为数据包提供源地址和目标地址&#xff0c;从而实现网络通信。ICMP协议&#xff1a;ICMP&#xff08…...

【Java 面试 八股文】Redis篇

Redis 1. 什么是缓存穿透&#xff1f;怎么解决&#xff1f;2. 你能介绍一下布隆过滤器吗&#xff1f;3. 什么是缓存击穿&#xff1f;怎么解决&#xff1f;4. 什么是缓存雪崩&#xff1f;怎么解决&#xff1f;5. redis做为缓存&#xff0c;mysql的数据如何与redis进行同步呢&…...

嵌入式EasyRTC实时通话支持海思hi3516cv610,编译器arm-v01c02-linux-musleabi-gcc

EasyRTC已经完美支持海思hi3516cv610&#xff0c;编译器arm-v01c02-linux-musleabi-gcc&#xff0c;总体SDK大小控制在680K以内&#xff08;预计还能压缩100K上下&#xff09;&#xff1a; EasyRTC在hi3516cv610芯片上能双向通话、发送文字以及二进制指令&#xff0c;总体运行…...

自然语言处理NLP入门 -- 第四节文本分类

目标 本章的目标是帮助你理解文本分类的基本概念&#xff0c;并通过具体示例学习如何使用 scikit-learn 训练文本分类模型&#xff0c;以及如何利用 OpenAI API 进行文本分类。 5.1 什么是文本分类&#xff1f; 文本分类&#xff08;Text Classification&#xff09;是自然语…...

深入解析:如何在C#和C/C++之间安全高效地通过P/Invoke传递多维数组

在工业控制、机器人编程和物联网等领域&#xff0c;我们经常需要让C#这样的托管语言与C/C编写的底层库进行交互。在这个过程中&#xff0c;遇到需要传递多维数组的场景时&#xff0c;许多开发者会意外遭遇System.Runtime.InteropServices.MarshalDirectiveException异常。本文将…...

Spring Boot全局异常处理终极指南:从青铜到王者的实战演进

一、为什么需要全局异常处理&#xff1f; 在用户中心这类核心服务中&#xff0c;优雅的异常处理是系统健壮性的生命线。未处理的异常会导致&#xff1a; 服务雪崩&#xff1a;单点异常扩散到整个系统&#xff08;✖️&#xff09;信息泄露&#xff1a;暴露敏感堆栈信息&#…...

【Elasticsearch】match_bool_prefix查询

match_bool_prefix查询是 Elasticsearch 中一种用于全文搜索的查询方式&#xff0c;适用于需要同时匹配多个词汇&#xff0c;但词汇顺序不固定的情况&#xff0c;它结合了布尔查询&#xff08;bool&#xff09;和前缀查询&#xff08;prefix&#xff09;的功能&#xff0c;适用…...

Unity-Mirror网络框架-从入门到精通之Multiple Additive Scenes示例

文章目录 前言Multiple Additive Scenes示例Additive Scenes示例MultiSceneNetManagerPhysicsCollisionRewardSpawner总结前言 在现代游戏开发中,网络功能日益成为提升游戏体验的关键组成部分。本系列文章将为读者提供对Mirror网络框架的深入了解,涵盖从基础到高级的多个主题…...

[开源]MaxKb+Ollama 构建RAG私有化知识库

MaxKbOllama&#xff0c;基于RAG方案构专属私有知识库 关于RAG工作原理实现方案 一、什么是MaxKb&#xff1f;二、MaxKb的核心功能三、MaxKb的安装与使用四、MaxKb的适用场景五、安装方案、 docker版Docker Desktop安装配置MaxKb安装和配置 总结和问题 MaxKB 是一款基于 LLM 大…...

Ubuntu 下 nginx-1.24.0 源码分析 - NGX_HAVE_GETTIMEZONE 宏

表示当前平台支持通过 gettimezone() 直接获取时区偏移值&#xff08;以分钟为单位&#xff09; 该宏用于适配不同操作系统对时区信息获取方式的差异。 当 NGX_HAVE_GETTIMEZONE 被定义时&#xff0c;Nginx 会调用 ngx_gettimezone() 获取时区偏移 在 Ubuntu 环境下&#xff0c…...

数据结构之队列,哈希表

一 队列(先进先出) 1.定义&#xff1a;从一端进行数据插入&#xff0c;另一端进行删除的线性存储结构 队列类型 常见操作 - 入队&#xff08;Enqueue&#xff09;&#xff1a;将新元素添加到队列的尾部。若队列有空间&#xff0c;新元素会成为队列的新尾部元素&#xff1b;若…...

缓存的介绍

相关面试题 &#xff1a; ● 为什么要用缓存&#xff1f; ● 本地缓存应该怎么做&#xff1f; ● 为什么要有分布式缓存?/为什么不直接用本地缓存? ● 为什么要用多级缓存&#xff1f; ● 多级缓存适合哪些业务场景&#xff1f; 缓存思想 空间换时间(索引&#xff0c;集群&a…...

372_C++_当有多个通道,开启不同告警的同一种的开关时,限制该开关的打开数量(比如视频上传开关)

GetCloudUploadNum函数 GetCloudUploadNum 函数主要用于统计和控制云端视频上传的通道数量,其主要功能如下: 功能目的// 检查每个通道的云端视频上传配置,并统计启用云端上传的通道总数 int CloudUploadNum = 0; bool InValidCloudUploadChn[MAX_CHN_NUMPARA] = {};...

Jenkins 配置 Git Parameter 四

Jenkins 配置 Git Parameter 四 一、开启 项目参数设置 勾选 This project is parameterised 二、添加 Git Parameter 如果此处不显示 Git Parameter 说明 Jenkins 还没有安装 Git Parameter plugin 插件&#xff0c;请先安装插件 Jenkins 安装插件 三、设置基本参数 点击…...

Unity崩溃后信息结合符号表来查看问题

目录 SO文件符号表对调试和分析的重要性调试方面分析方面 错误数据安装Logcat解释符号表设置符号文件路径生成解析 相关参考 SO文件 so 文件&#xff08;Shared Object File&#xff0c;共享目标文件&#xff09;和符号表紧密相关&#xff0c;它们在程序的运行、调试和分析过程…...

【云安全】云原生- K8S API Server 未授权访问

API Server 是 Kubernetes 集群的核心管理接口&#xff0c;所有资源请求和操作都通过 kube-apiserver 提供的 API 进行处理。默认情况下&#xff0c;API Server 会监听两个端口&#xff1a;8080 和 6443。如果配置不当&#xff0c;可能会导致未授权访问的安全风险。 8080 端口…...

【Matlab算法】基于人工势场的多机器人协同运动与避障算法研究(附MATLAB完整代码)

📚基于人工势场的多机器人协同运动与避障算法研究 摘要1. 引言2. 方法说明2.1 人工势场模型2.2 运动控制流程3. 核心函数解释3.1 主循环结构3.2 力计算函数4. 实验设计4.1 参数配置4.2 测试场景5. 结果分析5.1 典型运动轨迹5.2 性能指标6. 总结与建议成果总结改进方向附录:完…...

Django项目中创建app并快速上手(pycharm Windows)

1.打开终端 我选择的是第二个 2.运行命令 python manage.py startapp 名称 例如&#xff1a; python manage.py startapp app01 回车&#xff0c;等待一下&#xff0c;出现app01的文件夹说明创建成功 3.快速上手 1.app注册 增加一行 "app01.apps.App01Config"&#…...

前端骨架怎样实现

前端骨架屏&#xff08;Skeleton Screen&#xff09;是一种优化页面加载体验的技术&#xff0c;通常在内容加载时展示一个简易的占位符&#xff0c;避免用户看到空白页面。骨架屏通过展示页面结构的骨架样式&#xff0c;让用户有页面正在加载的感觉&#xff0c;而不是等待内容加…...

服务器使用centos7.9操作系统前需要做的准备工作

文章目录 前言1.操作记录 总结 前言 记录一下centos7.9操作系统的服务器在部署业务服务之前需要做的准备工作。 大家可以复制到自己的编辑器里面&#xff0c;有需求的注释一些步骤。 备注&#xff1a;有条件的项目推荐使用有长期支持的操作系统版本。 1.操作记录 # 更换阿里云…...

【git-hub项目:YOLOs-CPP】本地实现01:项目构建

目录 写在前面 项目介绍 最新发布说明 Segmentation示例 功能特点 依赖项 安装 克隆代码仓库 配置 构建项目 写在前面 前面刚刚实现的系列文章: 【Windows/C++/yolo开发部署01】 【Windows/C++/yolo开发部署02】 【Windows/C++/yolo开发部署03】 【Windows/C++/yolo…...

oracle表分区--范围分区

文章目录 oracle表分区分区的原因分区的优势oracle表分区的作用oracle表分区类型一、范围分区二、 创建分区表和使用&#xff1a;1、按照数值范围划分2、按照时间范围3、MAXVALUE2. 向现有表添加新的分区3、 分区维护和重新组织&#xff08;合并/删除&#xff09; oracle表分区…...

第12周:LSTM(火灾温度)

1.库以及数据的导入 1.1库的导入 import torch.nn.functional as F import numpy as np import pandas as pd import torch from torch import nn1.2数据集的导入 data pd.read_csv("woodpine2.csv")dataTimeTem1CO 1Soot 100.00025.00.0000000.00000010.22825.…...

Xlua中C#引用Lua变量,导致Lua侧的GC无法回收的原因及解决方法

1. 引用关系导致&#xff1a; 在 XLua 中&#xff0c;当 C# 端引用了 Lua 变量时&#xff0c;Lua 的垃圾回收器&#xff08;GC&#xff09;不会回收这些被引用的变量。这是因为 Lua 的 GC 机制是基于引用计数和标记 - 清除算法的。当 C# 端持有对 Lua 变量的引用时&#xff0c;…...

Win10系统IP地址修改_出现了一个意外的情况不能完成所有你在设置中所要求的更改---Windows工作笔记001

今天在修改win10系统中的ip地址的时候报错了 来看看如何解决吧,挺简单,简单记录一下 这个时候就需要使用cmd命令来修改 一定要使用,管理员权限,运行cmd才可以 然后首先: 输入 netsh 然后输入 ip 然后输入: set address "以太网" 172.19.126.199 255.255.255.0…...

Generate html

"Generate HTML"&#xff08;生成 HTML&#xff09;指的是通过程序或工具自动创建 HTML 代码的过程。HTML&#xff08;超文本标记语言&#xff09;是用于创建网页内容和结构的标准语言。生成 HTML 通常意味着通过某些方式自动化地构建或生成网页的结构和元素&#xf…...

使用DeepSeek和Kimi快速自动生成PPT

目录 步骤1&#xff1a;在DeepSeek中生成要制作的PPT主要大纲内容。 &#xff08;1&#xff09;在DeepSeek网页端生成 &#xff08;2&#xff09;在本地部署DeepSeek后&#xff0c;使用chatBox生成PPT内容 步骤2&#xff1a;将DeepSeek成的PPT内容复制到Kimi中 步骤3&…...

全面了解HTTP(一)

全面了解HTTP&#xff08;二&#xff09;-CSDN博客 web及网络基础 使用HTTP协议访问web&#xff1a; HTTP: 网络基础TCP/IP 与HTTP关系密切的协议&#xff1a;IP,TCP,DNS 负责域名解析的DNS服务 各种协议与HTTP协议的关系 URI和URL 简单的HTTP协议 HTTP协议用于客户端和服…...

PCDN“无盘业务”

PCDN&#xff08;Proactive Content Delivery Network&#xff09;的无盘业务&#xff0c;相对于有盘业务而言&#xff0c;主要指的是在不依赖本地磁盘存储的情况下进行内容分发。在PCDN网络中&#xff0c;无盘业务通常依赖于内存或其他形式的非持久性存储来缓存和分发内容。以…...

【LLM】13:大模型算法面试题库

一、Transformer篇 1. Transformer的结构是什么 Transformer 由 编码器&#xff08;Encoder&#xff09; 和 解码器&#xff08;Decoder&#xff09; 两部分组成&#xff0c;两部分都包含目标嵌入层、位置编码、多头注意力层、前馈网络&#xff0c;且有残差连接和层归一化&am…...

redis集群

文章目录 节点启动节点集群数据结构CLUSTER MEET命令的实现 槽指派记录节点的槽指派信息专播节点的槽指派信息17.2.3记录集群所有槽的指派信息CLUSTER ADDSLOTS命令的实现 在集群中执行命令计算键属于哪个槽判断槽是否由当前节点负责处理MOVED错误节点数据库的实现 重新分片ASK…...

Spring Boot整合DeepSeek实现AI对话(API调用和本地部署)

本篇文章会分基于DeepSeek开放平台上的API&#xff0c;以及本地私有化部署DeepSeek R1模型两种方式来整合使用。 本地化私有部署可以参考这篇博文 全面认识了解DeepSeek利用ollama在本地部署、使用和体验deepseek-r1大模型 Spring版本选择 根据Spring官网的描述 Spring AI是一…...

git如何把多个commit合成一个

在 Git 中&#xff0c;如果你想把多个提交&#xff08;commit&#xff09;合并成一个&#xff0c;可以使用 git rebase 或 git reset 来完成。下面是两种常用方法&#xff1a; 方法一&#xff1a;使用 git rebase&#xff08;推荐&#xff09; git rebase 是合并多个提交为一…...

flutter启动后不显示文字,中文字体不显示

Flutter 3.29.0-1.0.pre.114运行报错&#xff1a; Failed to load font Roboto at https://fonts.gstatic.com/s/roboto/v32/KFOmCnqEu92Fr1Me4GZLCzYlKw.woff2 解决&#xff1a; 下载roboto字体&#xff0c;并将字体&#xff08;Roboto-Regular.ttf&#xff09;放入app代码下…...

跟着李沐老师学习深度学习(十一)

经典的卷积神经网络 在本次笔记中主要介绍一些经典的卷积神经网络模型&#xff0c;主要包含以下&#xff1a; LeNet&#xff1a;最早发布的卷积神经网络之一&#xff0c;目的是识别图像中的手写数字&#xff1b;AlexNet&#xff1a; 是第一个在大规模视觉竞赛中击败传统计算机…...

小结:OSPF的网络类型,LSA

OSPF&#xff08;Open Shortest Path First&#xff09;是一个基于链路状态的内部网关协议&#xff08;IGP&#xff09;。以下是对OSPF网络类型、LSA类型、序列号与Age作用&#xff0c;以及相关配置指令的详细讲解。 一、OSPF的网络类型 OSPF支持多种网络类型&#xff0c;不同…...

用vue3写一个好看的wiki前端页面

以下是一个使用 Vue 3 Element Plus 实现的 Wiki 风格前端页面示例&#xff0c;包含现代设计、响应式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 头部导航 --><el-header class"wiki-header"><d…...

Redis常见数据结构

目录 基本介绍 特点&#xff1a; 全局命令 数据类型&#xff1a; String Hash List Set Zset 基本介绍 Redis是一个在内存中存储数据的中间件,可作为数据库&#xff0c;缓存&#xff0c;消息队列等。 特点&#xff1a; 持久化&#xff1a;Redis会把数据存储在内存中…...

JavaScript中Map和Set数据结构详解

目录 一、Map 1. 定义与特点 2. 创建Map对象 3. 常用方法 4. 遍历Map 5. Map与对象的区别 二、Set 1. 定义与特点 2. 创建Set对象 3. 常用方法 4. 遍历Set 5. 应用场景 数据去重 集合运算 遍历集合 一、Map 1. 定义与特点 Map 是 JavaScript ES6 中新增的一种数…...

【devops】 Git仓库如何fork一个私有仓库到自己的私有仓库 | git fork 私有仓库

一、场景说明 场景&#xff1a; 比如我们Codeup的私有仓库下载代码 放入我们的Github私有仓库 且保持2个仓库是可以实现fork的状态&#xff0c;即&#xff1a;Github会可以更新到Codeup的最新代码 二、解决方案 1、先从Codeup下载私有仓库代码 下载代码使用 git clone 命令…...

deepseek:三个月备考高级系统架构师

一、备考总体规划&#xff08;2025年2月11日 - 2025年5月&#xff09; 1. 第一阶段&#xff1a;基础夯实&#xff08;2025年2月11日 - 2025年3月10日&#xff09; 目标&#xff1a;快速掌握系统架构师考试的核心知识点。 重点内容&#xff1a; 计算机组成原理、操作系统、数据…...

设计模式Python版 命令模式(上)

文章目录 前言一、命令模式二、命令模式示例 前言 GOF设计模式分三大类&#xff1a; 创建型模式&#xff1a;关注对象的创建过程&#xff0c;包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。结构型模式&#xff1a;关注类和对象之间的组合&…...

部署 DeepSeek R1各个版本所需硬件配置清单

DeepSeek-R1 通过其卓越的推理性能和灵活的训练机制&#xff0c;在 2025 年的春节期间受到了广泛关注。 DeepSeek-R1 是一款高性能的 AI 推理模型&#xff0c;主要通过强化学习技术来增强模型在复杂任务场景下的推理能力。 在本地部署 DeepSeek-R1 时&#xff0c;尤其是完整的…...

【NLP 21、实践 ③ 全切分函数切分句子】

当无数个自己离去&#xff0c;我便日益坦然 —— 25.2.9 一、jieba分词器 Jieba 是一款优秀的 Python 中文分词库&#xff0c;它支持多种分词模式&#xff0c;其中全切分方式会将句子中所有可能的词语都扫描出来。 1.原理 全切分方式会找出句子中所有可能的词语组合。对于一…...

pycharm如何对比两个文档

在 PyCharm 中对比两个文档&#xff0c;并使它们同步滚动&#xff0c;可以按照以下步骤操作&#xff1a; 在 PyCharm 的文件列表中右键点击需要对比的其中一个文件。选择 “Compares With” 选项。选择第二个文件进行对比。...

React 第二十五节 <Fragment></Fragment> 的用途以及使用注意事项详解

文章如果错误偏差&#xff0c;烦请及时批评指正 一、为什么要使用 <Fragment>&#xff1f; 因为在 React 中&#xff0c;组件必须返回单个根元素。当我们尝试直接返回相邻的 JSX 元素时&#xff1a; function BrokenComponent() {return (<h1>标题</h1><…...

探秘Hugging Face与DeepSeek:AI开源世界的闪耀双子星

目录 一、引言&#xff1a;AI 开源浪潮的澎湃二、Hugging Face&#xff1a;AI 开源社区的基石&#xff08;一&#xff09;起源与发展历程&#xff08;二&#xff09;核心技术与特色&#xff08;三&#xff09;在 AI 领域的广泛应用 三、DeepSeek&#xff1a;东方崛起的 AI 新势…...

centos 和 ubuntu 区别

一、发行版与支持 1. CentOS 是基于 Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码重新编译而成的社区版&#xff0c;遵循开源协议。一般由社区进行维护&#xff0c;每 7 年左右发布一个主要版本&#xff0c;注重稳定性和长期支持&#xff0c;适合对系统稳定性…...