08、Java学习-面向对象中级:

Java学习第十二天——面向对象中级:

IDEA:

创建完新项目后,再src里面创建.java文件进行编写。

  • src——存放源码文件(.java文件);
  • out——存放编译后的字节码文件(.class文件)

在IDEA中,我们run一个文件时,会先编译成一个class文件,再运行。

常用快捷键:

  1. 删除当前行:ctrl + y;
  2. 复制当前行:ctrl + d;
  3. 补全代码:alt + /;
  4. 添加注释和取消注释:ctrl + /;(第一次是注释,后一次是取消注释);
  5. 导入改行需要的类:先配置 auto import,然后使用alt + enter即可;
  6. 快速格式化代码:ctrl + alt + L;
  7. 快速运行程序:alt + R;
  8. 快速生成构造器:(自定义为Alt + I了)Alt + Insert,选择Constructor,要选择属性的话要按住ctrl;
  9. 查看类的层级关系(学完类后很有用):把光标放在要查找的类上,然后ctrl + H;
  10. 定位方法(学完继承后很有用):将光标放到一个方法上,输入 ctrl + B,可以定位到方法;
  11. 自动分配变量名:通过再后面跟.var ,举例——new.Mytools().var
  12. 还有其余很多的快捷键,暂时不介绍。

模板(template)/自定义模板:

file -> settings -> editor -> Live templates -> 查看有哪些模板快捷键/可以自己增加模板。

模板可以高效地完成开发。

不过,建议新手暂时不用哦,还是再熟悉熟悉代码。


包:

为什么要有包?

因为我们在一个文件夹下不能有两个同名文件。

三大作用:

  1. 区分相同名字的类;
  2. 当类很多时,可以很好地管理类;
  3. 控制访问范围

包的基本语法:

package 包名;
class 类名{}
  1. package 为关键字,表示打包;
  2. 下面的内容表示在这个包内的内容。

包的本质:

其实就是创建不同的文件夹/目录来保存类文件。

在IDEA中,我们找到src,右键,找到package,点击,输入包名便创建好了。

包名里的"."表示第几级目录,如:com.xiaoming,com为xiaoming的上一级目录,其内含有子目录xiaoming

包的命名:

命名规则:

只能包含数字、字母、下划线、小圆点.,但不能用数字开头,不能是关键字或保留字

命名规范:

一般是小写字母+小圆点,一般是:

com.公司名.项目名.业务模块名

常用的包:

一个包下,包含很多的类,java中常用的包有:

  1. java.lang.*——lang是基本包,默认引用,不需要再引入;
  2. java.util.*——util包,系统提供的工具包,工具类,如Scanner;
  3. java.net.*——网络包,网络开发;
  4. java.awt.*——是做java的界面开发,GUI。

如何引用包?

有两种格式:

  1. import java.util.Scanner;这种格式就是只引入包下的一个特定的类;(import 包名.类名;)
  2. import java.util.*; 这种格式表示将java.util包所有都引入。(import 包名. *;)
package com.jiangxian.pkg;
import java.util.Arrays;
/*我们需要使用哪个类就引入哪个类就好了;尽量不要使用*号导入。
*/public class Import01 {public static void main(String[] args) {// 使用系统提供的Arrays完成数组排序;int[] arr = {-1, 20, 2, 13, 3};// 比如对其进行排序;// 传统方法是,自己编写排序函数(冒泡)// 系统是提供了相关的类,可以方便完成ArraysArrays.sort(arr);for(int i = 0; i < arr.length; i++){System.out.println(arr[i] + "\t");}}
}

注意事项和使用细节:

  1. package的作用是声明当前类所在的包,需要放再类(class)的最上面,一个类中最多有一句package;
  2. import指令 位置放置再package的下面,在类定义的前面,可以有多句且没有顺序要求。

访问修饰符:

基本介绍:

java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用public修饰,对外公开
  2. 受保护级别:用protected修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包中的类公开
  4. 私有级别:用private修饰,==只有类本身可以访问,不对外公开。==6
访问级别访问修饰控制符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

注意事项:

  1. 修饰符可以用来修饰属性,成员方法,和类(只有public和默认可以修饰类)
  2. 只有默认和public可以修饰类,且遵循上表的访问权限特点;
  3. 暂时没有学习继承,关于子类的访问权限,在下文展开;
  4. 成员方法的访问规则和属性完全一致。

面向对象编程三大特征:

基本介绍:

  1. 封装;
  2. 继承;
  3. 多态。

封装(Encapsulation)——第一大特征:

封装介绍:

封装(encapsulation)就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。

封装的理解和好处:

  1. 隐藏实现的细节;
  2. 可以对数据进行验证,保证安全合理性。
  3. 比如我们使用电脑,但是我们可以不用去知道电脑是怎么运行的。

封装的实现步骤:

  1. 属性私有化private;
  2. 提供一个public的set用于修改属性(Alt + I调出,选择Setter);
  3. 提供一个public的get方法,用于获取某个属性的值(Alt + I调出,选择Getter)

封装的快速入门:

package com.jiangxian.encap;public class Encapsulational01 {public static void main(String[] args) {Person person = new Person();person.setName("江弦");person.setAge(21);person.setSalary(30000);System.out.println(person.info());System.out.println(person.getSalary());// 使用构造器:Person person2 = new Person(80,"凤九",500000);System.out.println("=======凤九的信息=======");System.out.println(person2.info());}
}class Person{public String name;private int age;private double salary;public void say(int n, String name){}public Person(int age, String name, double salary) {this.age = age;this.name = name;this.salary = salary;// 将set写入构造器内,这样仍然可以验证。setAge(age);setSalary(salary);setName(name);}public Person() {}public String getName() {return name;}public void setName(String name) {if(name.length() >= 2 && name.length() <= 6){this.name = name;}else{this.name = "无名人";System.out.println("名字的长度不对,请输入(2-6)个字符的名字。");}}public int getAge() {return age;}public void setAge(int age) {if(age >=0 && age <= 150){this.age = age;}else{this.age = 0;System.out.println("你设置的年龄不正确。");}}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String info(){return "信息为 name=" + name + ", age=" + age + ", salary=" + salary;}
}

封装与构造器:

24.11.6

可以将封装与构造器结合,为什么要这样做呢,因为我们想要在构造器中也引入防控机制,如上文中:

    public Person(int age, String name, double salary) {//this.age = age;//this.name = name;//this.salary = salary;// 将set写入构造器内,这样仍然可以验证。setAge(age);setSalary(salary);setName(name);}

练习:

package com.jiangxian.encap;public class Account {private String name;private double balance;private String password;public Account(String name, double balance, String password) {setName(name);setBalance(balance);setPassword(password);}public Account() {}public String getName() {return name;}public void setName(String name) {if(name.length() >= 2 && name.length() <=4) {this.name = name;}else{this.name = "无名人";System.out.println("请输入2~4个字符的名字。");}}public double getBalance() {return balance;}public void setBalance(double balance) {if(balance > 20){this.balance = balance;}else{this.balance = 0;System.out.println("余额必须大于20。");}}public String getPassword() {return password;}public void setPassword(String password) {if(password.length() == 6){this.password = password;}else{this.password = "000000";// soutSystem.out.println("密码长度有误,你的默认密码是000000");}}public String info(){return "name=" + name + ", balance=" + balance + ", password=" + password;}
}
package com.jiangxian.encap;public class AccountTest {public static void main(String[] args) {Account account = new Account("jiangxian", 100, "123456");System.out.println(account.info());System.out.println("======江弦的账户======");Account jiangxian = new Account("江弦", 3080, "123456");System.out.println(jiangxian.info());}
}

继承(Extends)——第二大特征:

为什么需要继承?

从一个例子引入:

package com.jiangxian.extend_;
// 小学生 -》 模拟小学生考试的情况
public class Pupil {public String name;public int age;private double score;public void setName(String name) {this.name = name;}public void testing(){System.out.println("小学生 " + name + "正在考小学数学。");}public void info(){System.out.println("小学生名 " + name + " 年龄 " + age + " 成绩 " + score);}
}
package com.jiangxian.extend_;
// 大学生 -》 模拟大学生的考试情况
public class Graduate {public String name;public int age;private double score;public void setName(String name) {this.name = name;}public void testing(){System.out.println("大学生 " + name + "正在考大学数学。");}public void info(){ // 和小学生不一样的地方System.out.println("大学生名 " + name + " 年龄 " + age + " 成绩 " + score);}
}
package com.jiangxian.extend_;public class Extends01 {public static void main(String[] args) {Pupil pupil = new Pupil();pupil.name = "XiaoMing";pupil.age = 10;pupil.setScore(60);pupil.testing();pupil.info();System.out.println("======");Graduate graduate = new Graduate();graduate.name = "DaMing";graduate.age = 22;graduate.testing();graduate.setScore(100);graduate.info();}
}

我们发现,两个类的属性与方法有很多都是相同的,我们可以使用继承去解决我们的代码复用问题。

基本介绍:

继承可以解决代码复用,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。

基本语法:

class 子类 extends 父类{}
  1. 子类就会自动拥有父类定义的属性和方法;
  2. 父类叫做超类,基类(共有属性和共有方法);
  3. 子类又叫派生类(特有属性和特有方法)。

快速入门:

package com.jiangxian.extend_.improve_;
// 父类,是Pupil和Graduate的父类
public class Student {// 共有的属性与方法如下所示:public String name;public int age;private double score;public void setScore(double score) {this.score = score;}public void info(){System.out.println("学生名 " + name + " 年龄 " + age + " 成绩 " + score);}
}
package com.jiangxian.extend_.improve_;public class Pupil extends Student{public void testing(){System.out.println("小学生正在考小学数学..");}
}
package com.jiangxian.extend_.improve_;public class Graduate extends Student {public void testing(){System.out.println("大学生正在考大学数学..");}
}
package com.jiangxian.extend_.improve_;public class Test {public static void main(String[] args) {Pupil pupil = new Pupil();pupil.name = "XiaoMing";pupil.age = 10;pupil.setScore(60);pupil.testing();pupil.info();System.out.println("======");Graduate graduate = new Graduate();graduate.name = "DaMing";graduate.age = 22;graduate.testing();graduate.setScore(100);graduate.info();}
}

继承带来的便利:

  1. 代码的复用性提高了;
  2. 代码的扩展性和维护性提高了。

继承的细节:

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问(可以间接访问),要通过父类提供的公共的方法;

    package com.jiangxian.extend_;public class Base { // 父类// 四个不同修饰类型的四个属性public int n1 = 100;protected int n2 = 200;int n3 = 300;private int n4 = 400;// 无参构造器public Base(){System.out.println("base()....");}// 四个不同修饰符的方法public void test100(){System.out.println("test100()....");}protected void test200(){System.out.println("test200()....");}void test300(){System.out.println("test300()....");}private void test400(){System.out.println("test400()....");}// 父类提供一个public的方法:public int getN4() {return n4;}public void callTest400(){test400();}
    }
    package com.jiangxian.extend_;public class Sub extends Base{public Sub(){System.out.println("sub()...");}public void sayOk(){// 非私有的属性可以在子类直接访问;System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());// System.out.println(n4);test100();test200();test300();// test400callTest400();}
    }
    package com.jiangxian.extend_;public class ExtendsDetail {public static void main(String[] args) {Sub sub = new Sub();sub.sayOk();}
    }
  2. 子类必须调用父类的构造器,完成父类的初始化。

  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译会不通过。(第一个代码演示有默认构造器的情况,剩下两个代码演示没有默认构造器的情况)

    public class Sub extends Base{public Sub(){// 这里隐藏了一句super();这样就会默认调用系统的无参构造器System.out.println("sub()...");}public Sub(String name){System.out.println("sub(String name)...");}public void sayOk(){// 非私有的属性可以在子类直接访问;System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());// System.out.println(n4);test100();test200();test300();// test400callTest400();}
    }
    
    // 父类没有提供无参构造器
    package com.jiangxian.extend_;public class Base { // 父类// 四个不同修饰类型的四个属性public int n1 = 100;protected int n2 = 200;int n3 = 300;private int n4 = 400;// 无参构造器
    //    public Base(){
    //        System.out.println("base()....");
    //    }public Base(String name, int age){System.out.println("base(String, int age)....");}// 四个不同修饰符的方法public void test100(){System.out.println("test100()....");}protected void test200(){System.out.println("test200()....");}void test300(){System.out.println("test300()....");}private void test400(){System.out.println("test400()....");}// 父类提供一个public的方法:public int getN4() {return n4;}public void callTest400(){test400();}
    }
    package com.jiangxian.extend_;public class Sub extends Base{public Sub(){// 这里隐藏了一句super();这样就会默认调用系统的无参构造器super("Smith", 10);System.out.println("sub()...");}public Sub(String name){super("Tom", 100);System.out.println("sub(String name)...");}public void sayOk(){// 非私有的属性可以在子类直接访问;System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());// System.out.println(n4);test100();test200();test300();// test400callTest400();}
    }
  4. 若希望指定去调用父类的某个构造器,则显示的调用一下:super(参数列表);

    package com.jiangxian.extend_;public class Sub extends Base{public Sub(String name, int age){// 1. 调用父类的无参构造器,如下,或者什么都不写就会默认调用super():// super();// 父类的无参构造器// 2.调用父类的Base(String name)// super("江弦");// 3.调用父类的Base(String name, int age)super(name, age);System.out.println("Sub(String name, int age)...");}public Sub(){// 这里隐藏了一句super();这样就会默认调用系统的无参构造器super("Smith", 10);System.out.println("sub()...");}public Sub(String name){super("Tom", 100);System.out.println("sub(String name)...");}public void sayOk(){// 非私有的属性可以在子类直接访问;System.out.println(n1 + " " + n2 + " " + n3 + " " + getN4());// System.out.println(n4);test100();test200();test300();// test400callTest400();}
    }
  5. super在使用时,需要放在子类构造器的第一行,super关键字只能在构造器中使用;(先有爸爸,才能有儿子)

  6. super()和this()(this()是调用同类中的其它构造器的意思)都只能放在构造器的第一行,且这两个方法不能共存一个构造器;

  7. java所有类都是Object类的子类;

  8. 父类构造器的调用不限于直接父类!将一直往上追溯到Object类(顶级父类);

  9. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制(想要A继承B和C只能是B继承C,然后A再继承B);

  10. 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(Person is a Music)。

继承的本质:

(294/910)

当子类对象创建好后,建立查找关系。

super关键字:

super代表父类的引用,用于访问父类的属性、方法、构造器

基本语法:

  1. 访问父类的属性,但不能访问父类的private属性:super.属性名;
  2. 访问父类的方法,不能访问父类的private方法:super.方法(参数列表);
  3. 访问父类的构造器:super(参数列表);只能放在构造器的第一句,只能出现一句。

super给编程带来的便利/细节:

  1. 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化);
  2. 当子类中有和父类的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。若没有重名,使用super、this、直接访问时一样的效果;
  3. super访问不限于直接父类,若爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;若多个基类中都有重名的成员,使用super访问就近原则。

super和this的比较:

No.区别点superthis
1访问属性从父类开始查找属性访问本类的属性,如果本类没有此属性则从父类中继续查找
2调用方法从父类开始查找方法访问本类中的方法,如果本类没有此方法则从父类继续查找
3调用构造器调用父类构造器,必须放在子类构造器的首行调用本类构造器,必须放在构造器的首行
4特殊子类访问父类的对象表示当前对象

例子:

package com.jiangxian.super_;public class Base {public int n1 = 999;public int age = 111;public void cal(){System.out.println("Base类的cal()...");}public void eat(){System.out.println("Base类的eat()...");}
}
package com.jiangxian.super_;public class A extends Base{// 4个属性:public int n1 = 100;protected int n2 = 200;int n3 = 300;private int n4 = 400;public A(){}public A(String name){}public A(String name, int age){}//    public void cal(){
//        System.out.println("A类的cal()...");
//    }public void test100(){}protected void test200(){}void test300(){}private void test400(){}
}
package com.jiangxian.super_;public class B extends A{public int n1 = 888;// 编写测试方法:public void test(){// super的访问不限于直接父类,若爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;// 若多个基类(上级类)中都有同名的成员,使用super访问就近原则。System.out.println("super.n1=" + super.n1);super.cal();}public void hi(){System.out.println(super.n1 + " " + super.n2 + " " + super.n3);}public void cal(){System.out.println("B类的cal()...");}public void sum(){System.out.println("B类的sum()...");// 希望调用父类A的cal;// 这时,因为子类B没有cal方法,因此我可以使用下面三种方式// 找cal方法时(cal()和this.cal()),顺序:// 1.先找本类,若有,则调用;// 2.若没有,则找父类(若有,且可以调用,则调用)// 3.若父类没有,则继续找父类的父类,整个规则都是一样的,一直找到Object类// 提示:若查找方法的过程中,找到了,但不能访问(private),则报错,cannot access//      若查找方法的过程中,没有找到,则提示方法不存在。// cal();this.cal();// 找cal方法(super.cal())的顺序是直接查找父类,其他的规则一样// super.cal()// 演示访问属性的规则:// 和方法一样。System.out.println(n1);System.out.println(this.n1);// 查找父类的:System.out.println(super.n1);}public void ok(){super.test100();super.test200();super.test300();// super.test400(); 不能直接访问父类的private方法}public B(){super("jack");}
}
package com.jiangxian.super_;public class Super01 {public static void main(String[] args) {B b = new B();b.test();}
}

方法重写/覆盖(override):

基本介绍:

方法重写就是子类有一个方法,和父类的某个方法的名称、返回类型、参数都一样,那么,我们就说子类的这个方法覆盖了父类的方法。

快速入门:

package com.jiangxian.override_;public class Animal {public void cry(){System.out.println("动物叫唤...");}
}
package com.jiangxian.override_;public class Dog extends Animal{// 1.Dog是Animal子类;// 2.Dog的cry方法和Animal类的cry定义形式一样(名称、返回类型、参数)// 3.这时我们就说Dog的cry方法,重写了Animal的cry方法public void cry(){System.out.println("小狗在叫...");}
}
package com.jiangxian.override_;public class Override01 {public static void main(String[] args) {Dog dog = new Dog();dog.cry();}
}

注意事项和使用细节:

需要满足以下条件:

  1. 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样
  2. 子类方法的返回类型和父类方法返回类型一样,或者是父类发返回类型的子类,例如:父类 返回类型是Object,子类方法返回类型是String
    1. public Object getInfo();父类
    2. public String getInfo();子类
  3. 子类方法不能缩小父类方法的访问权限。
    1. void sayOk();
    2. public void sayOk();
package com.jiangxian.override_;public class Animal {public void cry(){System.out.println("动物叫唤...");}public Object m1(){return null;}public String m2(){return null;}public AAA m3(){return null;}protected void eat(){}
}
package com.jiangxian.override_;public class Dog extends Animal{// 1.Dog是Animal子类;// 2.Dog的cry方法和Animal类的cry定义形式一样(名称、返回类型、参数)// 3.这时我们就说Dog的cry方法,重写了Animal的cry方法public void cry(){System.out.println("小狗在叫...");}// 细节:子类方法的返回类型和父类方法返回类型一样,//      或者是父类返回类型的子类。public String m1(){ //构成重写,因为String是Object的子类return null;}// 报错,提示一个不兼容的返回类型
//    public Object m2(){
//        return null;
//    }public BBB m3(){return null;}public void eat(){}// protected void eat(){}
}
class AAA{
}class BBB extends AAA {}
package com.jiangxian.override_;public class Override01 {public static void main(String[] args) {Dog dog = new Dog();dog.cry();}
}

和重载的区别:

名称发生范围方法名形参列表返回类型修饰符
重载(overload)本类必须一样至少有一个不同无要求无要求
重写(override)父子类必须一样全部相同子类重写的方法,返回类型和父类一致或者是父类返回类型的子类。子类方法不能缩小父类方法的访问范围。

多态(polymorphic)——第三大特征(难点):

考虑一个喂食问题,假设有一个动物园,里面每只动物都需要喂食且喂食的东西种类都不一样,虽然都是喂食,但实际喂食的东西不一样。

我们可以使用重载函数的方式去完成,但是不利于我们维护和管理(当动物很多的时候)。

package com.jiangxian.poly_;public class Food {private String name;public Food(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
package com.jiangxian.poly_;public class Master {private String name;public Master(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}// 主任给小狗喂食:public void feed(Dog dog, Bone bone){System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());}// 主人给小猫喂食:public void feed(Cat cat, Fish fish){System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());}
}
package com.jiangxian.poly_;public class Animal {private String name;public Animal(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
package com.jiangxian.poly_;public class Cat extends Animal{public Cat(String name) {super(name);}
}
package com.jiangxian.poly_;public class Dog extends Animal{public Dog(String name) {super(name);}
}
package com.jiangxian.poly_;public class Fish extends Food{public Fish(String name) {super(name);}
}
package com.jiangxian.poly_;public class Bone extends Food{public Bone(String name) {super(name);}
}

为了解决代码的复用性不高,且不利于代码维护,我们引入了多态的概念。

多态的基本介绍:

方法或对象具有多种形态,是面向对象的第三大特征,多态是建立再封装和继承的基础上的。

多态的具体体现:

  1. 方法的多态(重写和重载体现多态);

  2. 对象的多态(核心,困难,重点):

    1. 一个对象的编译类型和运行类型可以不一致;
    2. 编译类型在定义对象时,就确定了,不能改变;
    3. 运行类型是可以变化的;
    4. 编译类型看定义时 = 号左边,运行类型看 = 右边;
    5. Animal animal = new Dog(); animal的编译类型时Animal,运行类型是Dog
    6. animal = new Cat();animal的运行类型现在变为了Cat,编译类型任然是Animal
    package com.jiangxian.poly_.objpoly_;public class Animal {public void cry(){System.out.println("Animal is crying...");}
    }
    package com.jiangxian.poly_.objpoly_;public class Dog extends Animal{@Override // 注解public void cry() {System.out.println("Dog is crying...");}
    }
    package com.jiangxian.poly_.objpoly_;public class Cat extends Animal{@Overridepublic void cry() {System.out.println("cat is crying...");}
    }
    package com.jiangxian.poly_.objpoly_;public class PolyObject {public static void main(String[] args) {// animal 的编译类型是 Animal,运行类型 AnimalAnimal animal = new Animal();animal.cry(); // 执行到这行时,animal的运行类型是Animal,运行Animal的cry// animal1 的编译类型是 Animal,运行类型是DogAnimal animal1 = new Dog();animal1.cry();// 执行到这行时,animal1的运行类型是Dog,运行Dog的cryAnimal animal2 = new Cat();animal2.cry();// 编译类型仍然是 Animal,但现在的运行类型为 Dog;animal = new Dog();animal.cry();// 执行Dog的cry}
    }

    父类的对象引用可以指向子类对象,运行时以运行类型为主。

    披着羊皮的狼(羊——编译类型,狼——运行类型)

快速入门:

现在我们来更改之前的代码:

package com.jiangxian.poly_;public class Master {private String name;public Master(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}// 使用多态机制:// animal 的编译类型是Animal,可以接收(指向)Animal和Animal子类的对象// food 的编译类型是 Food,可以接收(指向)Food 和 Food子类的对象public void feed(Animal animal, Food food){System.out.println("主人 " + name + " 给 " + animal.getName() + " 吃 " + food.getName());}//    // 主任给小狗喂食:
//    public void feed(Dog dog, Bone bone){
//        System.out.println("主人 " + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
//    }
//    // 主人给小猫喂食:
//    public void feed(Cat cat, Fish fish){
//        System.out.println("主人 " + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
//    }
}

多态的注意事项和细节讨论:

多态的前提:两个对象存在继承关系(建立在继承和封装的基础上);

多态的向上转型。

  1. 本质:父类的引用指向了子类的对象;
  2. 语法:父类类型 引用名 = new 子类类型();
  3. 特点,编译类型看左边,运行类型看右边。
    1. 可以调用父类中的所有成员(需遵守访问权限,无法使用private);
    2. 不能调用子类中特有成员(因为在编译阶段,能调用哪些成员由编译类型决定);
    3. 最终运行结果看子类的具体实现(若子类没有,就找父类,和前面super的调用是一样的)。

多态的向下转型:

  1. 语法:子类类型 引用名 = (子类类型) 父类引用;

  2. 只能强转父类的引用,不能强转父类的对象;

    Animal animal = new Cat(); // animal是父类的引用,指向的确实是Cat类型的对象。
    Cat cat = (Cat) animal;
    
  3. 要求父类的引用必须是指向当前目标类型的对象;(披着羊皮的狼是狼,不会是老虎。)

  4. 当向下转型后,可以调用子类类型中所有的成员。

属性的重写问题:

属性没有重写之说,属性的值看编译类型。

instanceof 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型(判断的是运行类型),若是,返回true,否则为false。

动态绑定机制(非常非常重要):

  1. 调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  2. 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
package com.jiangxian.poly_.dynamic;public class A {public int i = 10;public int sum(){return getI() + 10;}public int sum1(){return i + 10;}public int getI(){ // 父类有getIreturn i;}
}
package com.jiangxian.poly_.dynamic;public class B extends A{public int i = 20;
//    public int sum(){
//        return i + 20;
//    }public int getI(){ // 子类也有getIreturn i;}public int sum1(){return i + 10;}
}
package com.jiangxian.poly_.dynamic;public class DynamicBinding {public static void main(String[] args) {A a = new B();System.out.println(a.sum()); // 发生动态绑定// 1.看a的运行类型是什么,发现时B类型的,所以调用子类的getI;// 2.getI是返回属性,没有动态绑定,所以返回的是子类的i// 3.由于子类没有sum函数,所以使用父类的,答案为30System.out.println(a.sum1());}
}

看到函数时要注意有没有动态绑定机制。

多态的应用:

1)多态数组:

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。

package com.jiangxian.poly_.polyarr_;public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = 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;}public String say(){return name + "\t" + age;}
}
package com.jiangxian.poly_.polyarr_;public class Student extends Person {private double score;public Student(String name, int age, double score) {super(name, age);this.score = score;}public double getScore() {return score;}public void setScore(double score) {this.score = score;}@Overridepublic String say() {return "学生"  + super.say() + "分数:" + score;}// 特有方法:public void study(){System.out.println("学生 " + getName() + " 正在学Java。");}
}
package com.jiangxian.poly_.polyarr_;public class Teacher extends Person{private double salary;public Teacher(String name, int age, double salary) {super(name, age);this.salary = salary;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}@Overridepublic String say() {return "教师 " + super.say() + " 薪水:" + salary;}// 特有方法:public void teach(){System.out.println("老师 " + getName() + " 正在学java课程。");}
}
package com.jiangxian.poly_.polyarr_;public class PolyArray {public static void main(String[] args) {Person[] persons = new Person[5];persons[0] = new Person("Jack", 20);persons[1] = new Student("Marry", 18, 100);persons[2] = new Student("Smith", 19, 31);persons[3] = new Teacher("Scott", 30, 20000);persons[4] = new Teacher("King", 50, 25000);for(int i = 0; i < persons.length; i++){System.out.println(persons[i].say());if(persons[i] instanceof Student){((Student)persons[i]).study();}else if(persons[i] instanceof Teacher){((Teacher)persons[i]).teach();}else if(persons[i] instanceof Person){}else{System.out.println("你输入的类型有误。");}}}}

2)多态参数:

方法定义的形参类型为父类类型,实参类型运行为子类类型。

例如:前面说的喂动物。

下面给出一个另外的例子:

package com.jiangxian.poly_.polyparameter_;public class Employee {private String name;private double salary;public Employee(String name, double salary) {this.name = name;this.salary = salary;}public String getName() {return name;}public double getSalary() {return salary;}public void setName(String name) {this.name = name;}public void setSalary(double salary) {this.salary = salary;}public double getAnnual(){return salary*12;}
}
package com.jiangxian.poly_.polyparameter_;public class Manage extends Employee {private double bonus;public Manage(String name, double salary, double bonus) {super(name, salary);this.bonus = bonus;}public double getBonus() {return bonus;}public void setBonus(double bonus) {this.bonus = bonus;}@Overridepublic double getAnnual() {return super.getAnnual() + bonus;}public void manage(){// 因为name是私有类型,所以只能通过getName来访问System.out.println("经理 " + getName() + " is managing.");}
}
package com.jiangxian.poly_.polyparameter_;public class Worker extends Employee{public Worker(String name, double salary){super(name, salary);}public void work(){System.out.println("工人 " + getName() + " is working.");}
}
package com.jiangxian.poly_.polyparameter_;public class PolyParameter {public static void main(String[] args) {Worker tom = new Worker("Tom", 2500);Manage milan = new Manage("Milan", 5000, 200000);PolyParameter plogParameter = new PolyParameter();plogParameter.showEmpAnnual(tom);plogParameter.showEmpAnnual(milan);plogParameter.testWork(tom);plogParameter.testWork(milan);}public void showEmpAnnual(Employee e){System.out.println(e.getAnnual());}public void testWork(Employee e){if(e instanceof Worker){((Worker)e).work();}else if(e instanceof Manage){((Manage)e).manage();}else{System.out.println("类型错误,请自检。");}}
}

Object类详解:

Object是所有类的父类,所以需要知道其内部有什么。

equals方法:

== 和 equals的对比:

== 是一个比较运算符:

  1. 既可以判断基本类型,也可以判断引用类型是否相等;
  2. 判断基本类型时,判断的是值是否相等;
  3. 判断引用类型时,判断地址是否相等,即判断是不是同一个对象(只关注运行类型,不管编译类型)。
package com.jiangxian.object_;public class Equals01 {public static void main(String[] args) {A a = new A();A b = a;A c = new B();A d = c;System.out.println(c == d);System.out.println(b == a);}
}class A{
}
class B extends A{
}

equals方法时Object类的一个方法:

  1. 只能用于判断引用类型;

  2. 默认判断的是地址是否相同,子类中往往重写该方法,用于判断内容是否相同。

        public boolean equals(Object obj) { // Object中的equals原码return (this == obj); // 使用== 判断,而用==判断引用类型就是判断地址是否相同,即是否是同一个对象}
    
        public boolean equals(Object obj) { // Integer中的equals原码if (obj instanceof Integer) { // 先判断obj是不是Integer的类型和子类型return value == ((Integer)obj).intValue(); // 若是,比较值的大小即可}return false; // 若不是Integer的类型或子类,或者值不相等直接返回False}
    

如何重写equals方法?

package com.jiangxian.object_;public class Person {private String name;private int age;public Person(String name, int age) {setName(name);setAge(age);}public String getName() {return name;}public void setName(String name) {if(name.length() >= 2 && name.length() <=6){this.name = name;}else{System.out.println("你输入的名字长度不合规,默认为无名。");this.name = "无名";}}public int getAge() {return age;}public void setAge(int age) {if(age >=0 && age <= 120){this.age = age;}else{System.out.println("你的年龄不符合生物学,默认为0岁");this.age = 0;}}public boolean equals(Person obj){if(obj == this){return true;}if(obj instanceof Person){Person person = (Person)obj;return this.name.equals(person.getName()) && this.age == person.getAge();}return false;}
}
package com.jiangxian.object_;public class Equals02 {public static void main(String[] args) {Person p1 = new Person("江弦", 21);Person p2 = new Person("江弦", 21);System.out.println(p1 == p2);System.out.println(p1.equals(p2));}
}

hashcode方法(在第二阶段深入,现在留个印象):

返回对象的哈希码值。支持此方法是为了提高哈希表(例如java.util.HashTable 提供的哈希表)的性能。

  1. 提高具有哈希结构的容器的效率;
  2. 两个引用,若指向同一个对象,则哈希值肯定是一样的;
  3. 两个引用,若指向的是不同的对象,则哈希值肯定是不一样的;
  4. 哈希值主要是根据地址号来的!不能完全将哈希值等价为地址;
  5. 以后在集合中,若hashCode需要的话,也可以进行重写。
package com.jiangxian.object_;public class HashCode_ {public static void main(String[] args) {AA aa = new AA();AA aa1 = new AA();AA aa2 = aa;System.out.println("aa.hashCode(): " + aa.hashCode());System.out.println("aa1.hashCode(): " + aa1.hashCode());System.out.println("aa2.hashCode(): " + aa2.hashCode());}
}class AA{}

toString方法:

基本介绍:

默认返回:全类名(包名与类名) + @ + 哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息。

    public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}
package com.jiangxian.object_;public class ToString_ {/*原码:(1)getClass().getName() 类的全类名(包名 + 类名)(2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/public static void main(String[] args) {Monster monster = new Monster("小妖怪", "巡山的", 1000);System.out.println(monster.toString() + "hashcode: " + monster.hashCode());}
}class Monster{private String name;private String job;private double sal;public Monster(String name, String job, double sal) {this.name = name;this.job = job;this.sal = sal;}
}

重写:

由于经常重写,其也被集成到了快捷键Alt + Insert(我更改为了Alt + I)。

package com.jiangxian.object_;public class ToString_ {/*原码:(1)getClass().getName() 类的全类名(包名 + 类名)(2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/public static void main(String[] args) {Monster monster = new Monster("小妖怪", "巡山的", 1000);System.out.println(monster.toString() + "hashcode: " + monster.hashCode());}
}class Monster{private String name;private String job;private double sal;public Monster(String name, String job, double sal) {this.name = name;this.job = job;this.sal = sal;}// 重写toString方法,输出对象的属性// 快捷键Alt + Insert,选择toString,默认是返回对象的属性值。@Overridepublic String toString() {return "Monster{" +"name='" + name + '\'' +", job='" + job + '\'' +", sal=" + sal +'}';}
}

直接输出一个对象时,toString方法会被默认的调用:

package com.jiangxian.object_;public class ToString_ {/*原码:(1)getClass().getName() 类的全类名(包名 + 类名)(2)Integer.toHexString(hashCode()) 将对象的hashCode值转成16进制字符串public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}*/public static void main(String[] args) {Monster monster = new Monster("小妖怪", "巡山的", 1000);System.out.println(monster.toString() + "hashcode: " + monster.hashCode());System.out.println(monster);}
}class Monster{private String name;private String job;private double sal;public Monster(String name, String job, double sal) {this.name = name;this.job = job;this.sal = sal;}// 重写toString方法,输出对象的属性// 快捷键Alt + Insert,选择toString,默认是返回对象的属性值。@Overridepublic String toString() {return "Monster{" +"name='" + name + '\'' +", job='" + job + '\'' +", sal=" + sal +'}';}
}

finalize方法:

  1. 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作(比如,对象打开了一个文件,这就是占用了一个资源,关闭文件就是释放资源)
  2. 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,正在销毁对象前,会先调用finalize方法;
  3. 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动出发垃圾回收机制。
  4. 在新版中,finalize已经被弃用了且在实际开发中不会使用,所以就不细看了。
package com.jiangxian.object_;public class Finalize_ {public static void main(String[] args){Car bmw = new Car("BMW"); // 现在有一个对象引用bmw指向Car对象bmw = null;// 现在将bmw指向 null, 那么之前创建的Car对象就没有人使用了,变为一个垃圾,// 垃圾回收器就会进行销毁,即把堆中Car的那个空间给释放出来了// 在销毁对象前,会调用该对象的finalize方法,我们可以在这个方法中,写一些自己的业务逻辑代码,比如释放资源(数据库连接,或者是打开的文件)// 若程序员不重写finalize方法,那么,就会调用Object的finalize方法// ,若程序员重写了finalize方法,就能实现自己的业务逻辑了。System.gc();}
}class Car{private String name;public Car(String name){this.name = name;}@Overrideprotected void finalize() throws Throwable {System.out.println("销毁汽车" + this.name);System.out.println("释放了某些资源。");}
}

如何查看原码:

在IDEA中,将光标放在想要查看原码的方法上,点击ctrl + b即可访问。

或者右键方法,点击 Go To -> Declaration or Usages 访问。


断点调试:

为什么要有断点调试?

断点调试能让我们一步步的看原码的执行过程,从而发现错误所在

在断点调试的过程中,是运行状态,是以对象的运行类型来执行的。

断点调试介绍:

  1. 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住(该行此时没有执行),然后,你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下,进而分析从而找到bug所在。
  2. 是程序员必须掌握的技能;
  3. 断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的Java水平。

断点调试的快捷键:

  1. F7:跳入方法内;
  2. F8:逐行执行代码;
  3. shift + F8:跳出方法;
  4. F9:执行到下一个断点。

断点调试应用:

01:

想在哪行加断点,就将鼠标挪动到行号处,单击鼠标即可。然后debug运行

package com.jiangxian.debug_;public class test01 {public static void main(String[] args) {int sum = 0;for(int i = 0; i < 10; i++){sum += i;System.out.println("i=" + i);System.out.println("sum=" + sum);}System.out.println("end..");}
}

02:

数组越界:

package com.jiangxian.debug_;public class test02 {public static void main(String[] args) {int arr[] = new int[5];for (int i = 0; i <= arr.length; i++) {System.out.println("arr[" + i + "] = " + arr[i]);}}
}

03:

进入JDK的方法源码,需要先配置一下:(要是不想看就自己勾选回来吧~)

  1. 点击Setting --> Build,Execution,Deployment --> Debugger --> Stepping;
  2. 把Do not step into the classes中的java.*,javax.*取消勾选,其他的随意。

但是进入源码可以会看晕,那么怎么出来呢?

可以使用shift + F8 跳出一层,重复,直到跳回我们进入的位置。

package com.jiangxian.debug_;import java.util.Arrays;public class test03 {public static void main(String[] args) {int[] arr = {1, -1, 10, -20, 100};// 这个例子是为了看源码Arrays.sort的实现;Arrays.sort(arr);for(int i = 0; i< arr.length; i++){System.out.println(arr[i]);}System.out.println("end..");}
}

04:

演示如何执行到下一个断点:

package com.jiangxian.debug_;import java.util.Arrays;public class test04 {public static void main(String[] args) {int[] arr = {8, -1 , 199, 70, 10};Arrays.sort(arr);for (int i = 0; i < arr.length; i++) {System.out.println(arr[i] + '\t');}System.out.println("hello100");// 断点System.out.println("hello200");System.out.println("hello300");System.out.println("hello400");System.out.println("hello500");System.out.println("hello600"); // 断点System.out.println("end...");}}

05:

查看对象的创建过程,以及动态继承的实现:

package com.jiangxian.debug_;public class DebugExercise {public static void main(String[] args) {// 创建对象的过程:// 1.加载Person类// 2.1默认初始化;2.显示初始化;3.构造器初始化// 3.返回对象的地址Person person = new Person("江弦",21);System.out.println(person);}
}class Person{private String name;private int age;public Person(){}public Person(String name, int age){this.name = name;this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}

零钱通:

模仿微信的零钱通:

项目的开发流程:

  • 项目需求说明:
    • 使用Java开发 零钱通项目,可以完成收益入账,消费,查看明细,退出系统等功能。
  1. 先完成菜单,并可以选择:
  2. 完成零钱通明细(即内部的功能)
  3. 完成收益入账;
  4. 完成消费;
  5. 完成退出功能;

项目开发说明:

  • 项目代码实现:
    • 先完成基本功能(过程编程);
    • 后改进为OOP。

面向过程的代码:

package com.jiangxian.smallchange;
import java.text.SimpleDateFormat;
import  java.util.Scanner;
import java.util.Date;public class SmallChangeSys {// 1.先完成显示菜单,并可以选择菜单,给出对应提示:public static void main(String[] args) {// 定义一个循环条件变脸loopboolean loop = true;// 为什么实现功能的选择,我们需要定义一个Scanner,与用户形成交互Scanner scanner = new Scanner(System.in);String key = "";// 2.实现明细:1、使用数组;2、适用对象;3、使用String拼接;// 为什么暂时这样定义就可以了呢,因为我们还没有收益入账,消费等,后续只要把这两个的内容拼接到details的后面就可以了String details = "---------------零钱通明细---------------";// 3.实现收益入账:要完成这个程序,驱动程序员增加新的变量和代码double money = 0.0;double balance = 0.0;Date date = null; // date是java.util.Date 类型,表示日期SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 可以用于日期格式化// 4.完成消费:完成这个功能,我们也需要增加一些变量:String note = "";// 让用户对是否退出做出判断:char choice;do{// 为什么用do while循环,因为菜单至少要显示一次System.out.println("\n===============零钱通菜单===============");System.out.println("\t\t\t1 零钱通明细");System.out.println("\t\t\t2 收益入账");System.out.println("\t\t\t3 消     费");System.out.println("\t\t\t4 退     出");System.out.println("请选择功能(1-4):");key = scanner.next();// 使用分支控制:switch(key){case "1":System.out.println(details);break;case "2":System.out.println("收益入账金额:");money = scanner.nextDouble();if (money < 0){System.out.println("你输入的金额有误,收益不应该为负数,请仔细确认!");break;}balance += money;date = new Date(); // 获取当前的日期,格式很奇怪,在循环前,应该要设置下格式details = details + "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance;break;case "3":System.out.println("消费金额为:");money = scanner.nextDouble();// money 的值需要校验,不能比余额大if (money > balance){System.out.println("你输入的金额有误,超过了你的剩余财产,请检验!");break;}System.out.println("输入消费说明:");note = scanner.next();date = new Date();balance -= money;details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;break;case "4":// 一段代码,一般只完成一个小功能,尽量不要混在一起,这样阅读起来比较轻松。System.out.println("你确认要退出吗?(y/n)");choice = scanner.next().charAt(0);while(choice != 'y' && choice != 'n'){System.out.println("你的输入有误,请输入yes或no,或者y或n!");choice = scanner.next().charAt(0);}if(choice == 'y'){loop = false;}break;default:System.out.println("你输入了零钱通没有的功能。");}}while(loop);System.out.println("你退出了零钱通。");}
}

OOP:

package com.jiangxian.smallchange;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;/*** 该类是完成零钱通的各个功能的类;* 使用OOP(面向对象编程);* 将各个功能对应一个方法。*/public class SmallChangeOOP {// 属性:怎么来的,就是我们面向过程里面设定的变量boolean loop = true;Scanner scanner = new Scanner(System.in);String key = "";String details = "---------------零钱通明细---------------";double money = 0.0;double balance = 0.0;Date date = null; // date是java.util.Date 类型,表示日期SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 可以用于日期格式化String note = "";char choice;// 方法是怎么来的,就是我们分析时候拆解的实现步骤// 先完成显示菜单,并可以选择:public void menu(){do{// 为什么用do while循环,因为菜单至少要显示一次System.out.println("\n===============选择零钱通菜单(OOP)===============");System.out.println("\t\t\t1 零钱通明细");System.out.println("\t\t\t2 收益入账");System.out.println("\t\t\t3 消     费");System.out.println("\t\t\t4 退     出");System.out.println("请选择功能(1-4):");key = scanner.next();// 使用分支控制:switch(key){case "1":this.detail();break;case "2":this.income();break;case "3":this.consume();break;case "4":// 一段代码,一般只完成一个小功能,尽量不要混在一起,这样阅读起来比较轻松。this.exit();break;default:System.out.println("你输入了零钱通没有的功能。");}}while(loop);System.out.println("你退出了零钱通。");}// 再完成零钱通明细:public void detail(){System.out.println(details);}// 完成收入:public void income(){System.out.println("收益入账金额:");money = scanner.nextDouble();if (money < 0){System.out.println("你输入的金额有误,收益不应该为负数,请仔细确认!");// break; 在方法里应该是return,即退出方法,不再执行return;}balance += money;date = new Date(); // 获取当前的日期,格式很奇怪,在循环前,应该要设置下格式details = details + "\n收益入账\t+" + money + "\t" + sdf.format(date) + "\t" + balance;}// 完成消费:public void consume(){System.out.println("消费金额为:");money = scanner.nextDouble();// money 的值需要校验,不能比余额大if (money > balance){System.out.println("你输入的金额有误,超过了你的剩余财产,请检验!");return;}System.out.println("输入消费说明:");note = scanner.next();date = new Date();balance -= money;details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t" + balance;}// 完成退出:public void exit(){System.out.println("你确认要退出吗?(y/n)");choice = scanner.next().charAt(0);while(choice != 'y' && choice != 'n'){System.out.println("你的输入有误,请输入yes或no,或者y或n!");choice = scanner.next().charAt(0);}if(choice == 'y'){loop = false;}}
}
package com.jiangxian.smallchange;public class SmallChangeSysApp {public static void main(String[] args) {new SmallChangeOOP().menu();}
}

24.11.9——(343/910),这周状态有点小差,另外这章真的好多,学的比较慢。。。明天把作业写了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/467814.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day41 | 动态规划 :完全背包应用 完全平方数单词拆分(类比爬楼梯)

Day41 | 动态规划 &#xff1a;完全背包应用 完全平方数&&单词拆分&#xff08;类比爬楼梯&#xff09; 动态规划应该如何学习&#xff1f;-CSDN博客 01背包模板 | 学习总结-CSDN博客 完全背包模板总结-CSDN博客 难点&#xff1a; 代码都不难写&#xff0c;如何想…

《 C++ 修炼全景指南:十九 》想懂数据库?深入 B 树的世界,揭示高效存储背后的逻辑

摘要 本文深入探讨了 B 树的原理、操作、性能优化及其实际应用。B 树作为一种平衡多路树结构&#xff0c;因其高效的查找、插入和删除操作广泛应用于数据库与文件系统中。文章首先介绍了 B 树的定义与性质&#xff0c;并详细阐述了节点分裂、合并等核心操作的实现方法。接着&a…

选择小练习

条件语句 if 条件语句&#xff0c;也叫作选择语句、判断语句。根绝特定条件判断是否成立&#xff0c;执行不同的语句段。简单来说&#xff0c;满足条件执行&#xff0c;不满足不执行。 条件语句是使用关键字 if 做判断&#xff0c;根据不同情况结合不同的关键字else 或者 eli…

单片机串口接收状态机STM32

单片机串口接收状态机stm32 前言 项目的芯片stm32转国产&#xff0c;国产芯片的串口DMA接收功能测试不通过&#xff0c;所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣&#xff0c;不过我的芯片已经主频跑…

BAAI 的 Aquila-VL-2B-llava-qwen: 促进视觉语言理解

简介 在人工智能领域&#xff0c;北京人工智能学会&#xff08;BAAI&#xff09;做出了重要贡献&#xff1a; 在人工智能领域&#xff0c;北京人工智能研究所&#xff08;BAAI&#xff09;开发的 Aquila-VL-2B-llava-qwen 模型做出了重大贡献。这一创新模型建立在 LLava-one-v…

测试实项中的偶必现难测bug--短信触发H5拒绝行为

问题描述: 企业邀请其他人加入团队,发送邀请短信给对方,对方通过短信链接跳转到H5页面,输入手机后,点击发送验证码,前提是短信通知验证弹窗需要打开,收到短信验证码后,点击一键代入,会触发拒绝加入行为。 需求: 由于我们的邀请链接是一次性的,一旦有用户确认加入或…

MCU的OTA升级(未完-持续更新)

1.术语 ISP : In-System Programming 在系统编程&#xff0c;是一种通过MCU&#xff08;微控制器单元&#xff09;上的内置引导程序&#xff08;BootLoader&#xff09;来实现对芯片内部存储器&#xff08;如Flash&#xff09;进行编程的技术。 华大目前对应的ISP IAP&…

即将盛大启幕“2025南京软件产业博览会·南京软博会”

在今年的南京软博会上&#xff0c;科技创新的浪潮再次席卷了整个会展现场&#xff0c;来自全球的软件产业精英们汇聚一堂&#xff0c;共同见证了软件产业的最新成果与未来趋势。随着云计算、大数据、人工智能等新兴技术的蓬勃发展&#xff0c;软件产业正站在一个前所未有的历史…

npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚本。

npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1&#xff0c;因为在此系统上禁止运行脚本。有关详细信息&#xff0c;请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Exe cution_Policies。 所在位置 行:1 字符: 1 npm install ~~~ CategoryInf…

网管平台(进阶篇):如何正确的管理网络设备?

网络设备作为构建计算机网络的重要基石&#xff0c;扮演着数据传输、连接和管理的关键角色。从交换机、路由器到防火墙、网关&#xff0c;各类网络设备共同协作&#xff0c;形成了高效、稳定的网络系统。本文将详细介绍网络设备的种类&#xff0c;并探讨如何正确管理这些设备&a…

【Linux】【Vim】多文件编辑与分屏

多文件编辑 编辑另一个文件文件列表分屏vimdiff文件跳转 编辑另一个文件 除了为每一个要编辑的文件运行一次 Vim 之外&#xff0c;还可以在当前 Vim 中开始编辑另一个文件。 :edit foo.txtVim 会关闭当前正在编辑的文件打开指定的新文件进行编辑。如果当前文件还有未存盘的内容…

从零开始训练一个大语言模型需要多少天?

一&#xff0c;前言 在AI领域&#xff0c;训练一个大型语言模型&#xff08;LLM&#xff09;是一个耗时且复杂的过程。几乎每个做大型语言模型&#xff08;LLM&#xff09;训练的人都会被问到&#xff1a;“从零开始&#xff0c;训练大语言模型需要多久和花多少钱&#xff1f;”…

潮玩宇宙方块兽系统开发:可定制UI与多种游戏内嵌助力个性化体验

潮玩宇宙方块兽系统开发正在推动潮玩与游戏的融合&#xff0c;通过个性化的UI设计和多游戏内嵌模式&#xff0c;为用户带来了独一无二的体验。本文将从可定制UI、多游戏内嵌功能以及系统实现等方面入手&#xff0c;探讨如何构建一个极具吸引力的潮玩宇宙方块兽系统。 一、可定制…

git提交顺序为什么是:add,conmmit,pull,push

git提交顺序为什么是&#xff1a;add,conmmit,pull,push 01. add,conmmit,pull,push的顺序问题02. 扩展&#xff1a;git上传常用的六个命令包括&#xff1a;add、commit、push、clone、pull、fetch。 add&#xff1a;将文件添加到暂存区 commit&#xff1a;将暂存区中的文件提交…

服务器数据恢复—EVA存储故障导致上层应用不可用的数据恢复案例

服务器存储数据恢复环境&#xff1a; 一台EVA某型号控制器EVA扩展柜FC磁盘。 服务器存储故障&检测&#xff1a; 磁盘故障导致该EVA存储中LUN不可用&#xff0c;导致上层应用无法正常使用。 服务器存储数据恢复过程&#xff1a; 1、将所有磁盘做好标记后从扩展柜中取出。硬…

解决编译 fast-lio-lc 算法时遇到的error方法

1.创建工作空间和下载 fast-lio-lc功能包 mkdir -p fast_lio_lc_ws/src cd fast_lio_lc_ws/src/ catkin_init_workspace git clone https://github.com/yanliang-wang/FAST_LIO_LC.git2.进入工作空间,编译 编译 fast-lio-lc遇到的error: 🕐error: fatal error: opencv/cv…

软件著作权申请教程(超详细)(2024新版)软著申请

目录 一、注册账号与实名登记 二、材料准备 三、申请步骤 1.办理身份 2.软件申请信息 3.软件开发信息 4.软件功能与特点 5.填报完成 一、注册账号与实名登记 首先我们需要在官网里面注册一个账号&#xff0c;并且完成实名认证&#xff0c;一般是注册【个人】的身份。中…

鸿蒙ArkTS中的布局容器组件(Scroll、List、Tabs)

1、Scroll组件 Scroll组件是一个可滚动的容器组件&#xff0c;用于在子组件的布局尺寸超过父组件尺寸时提供滚动功能。它允许在其内部容纳超过自身显示区域的内容&#xff0c;并通过滚动机制来查看全部内容。这对于显示大量信息&#xff08;如长列表、长篇文本或大型图像等&…

webWorker基本用法

我们都知道js是一个单线程的语言&#xff0c;当线程堵塞时&#xff0c;可能会导致页面无法正常交互&#xff0c;如一些复杂的可视化处理。即使是异步处理&#xff0c;也只是将其暂存到任务队列中去&#xff0c;等主线程执行完后依然会从任务队列中取过去。 为此&#xff0c;js提…

《手写Spring渐进式源码实践》实践笔记(第十八章 JDBC功能整合)

文章目录 第十八章 JDBC功能整合背景技术背景JDBC JdbcTemplate关键特性 用法示例业务背景 目标设计实现代码结构类图实现步骤 测试事先准备属性配置文件测试用例测试结果&#xff1a; 总结 第十八章 JDBC功能整合 背景 技术背景 JDBC JDBC&#xff08;Java Database Conne…