08、Java学习-面向对象中级:
Java学习第十二天——面向对象中级:
IDEA:
创建完新项目后,再src里面创建.java文件进行编写。
- src——存放源码文件(.java文件);
- out——存放编译后的字节码文件(.class文件)
在IDEA中,我们run一个文件时,会先编译成一个class文件,再运行。
常用快捷键:
- 删除当前行:ctrl + y;
- 复制当前行:ctrl + d;
- 补全代码:alt + /;
- 添加注释和取消注释:ctrl + /;(第一次是注释,后一次是取消注释);
- 导入改行需要的类:先配置 auto import,然后使用alt + enter即可;
- 快速格式化代码:ctrl + alt + L;
- 快速运行程序:alt + R;
- 快速生成构造器:(自定义为Alt + I了)Alt + Insert,选择Constructor,要选择属性的话要按住ctrl;
- 查看类的层级关系(学完类后很有用):把光标放在要查找的类上,然后ctrl + H;
- 定位方法(学完继承后很有用):将光标放到一个方法上,输入 ctrl + B,可以定位到方法;
- 自动分配变量名:通过再后面跟.var ,举例——
new.Mytools().var; - 还有其余很多的快捷键,暂时不介绍。
模板(template)/自定义模板:
file -> settings -> editor -> Live templates -> 查看有哪些模板快捷键/可以自己增加模板。
模板可以高效地完成开发。
不过,建议新手暂时不用哦,还是再熟悉熟悉代码。
包:
为什么要有包?
因为我们在一个文件夹下不能有两个同名文件。
三大作用:
- 区分相同名字的类;
- 当类很多时,可以很好地管理类;
- 控制访问范围
包的基本语法:
package 包名;
class 类名{}
- package 为关键字,表示打包;
- 下面的内容表示在这个包内的内容。
包的本质:
其实就是创建不同的文件夹/目录来保存类文件。
在IDEA中,我们找到src,右键,找到package,点击,输入包名便创建好了。
包名里的"."表示第几级目录,如:com.xiaoming,com为xiaoming的上一级目录,其内含有子目录xiaoming
包的命名:
命名规则:
只能包含数字、字母、下划线、小圆点.,但不能用数字开头,不能是关键字或保留字。
命名规范:
一般是小写字母+小圆点,一般是:
com.公司名.项目名.业务模块名。
常用的包:
一个包下,包含很多的类,java中常用的包有:
- java.lang.*——lang是基本包,默认引用,不需要再引入;
- java.util.*——util包,系统提供的工具包,工具类,如Scanner;
- java.net.*——网络包,网络开发;
- java.awt.*——是做java的界面开发,GUI。
如何引用包?
有两种格式:
- import java.util.Scanner;这种格式就是只引入包下的一个特定的类;(import 包名.类名;)
- 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");}}
}
注意事项和使用细节:
- package的作用是声明当前类所在的包,需要放再类(class)的最上面,一个类中最多有一句package;
- import指令 位置放置再package的下面,在类定义的前面,可以有多句且没有顺序要求。
访问修饰符:
基本介绍:
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
- 公开级别:用public修饰,对外公开;
- 受保护级别:用protected修饰,对子类和同一个包中的类公开;
- 默认级别:没有修饰符号,向同一个包中的类公开;
- 私有级别:用private修饰,==只有类本身可以访问,不对外公开。==6
| 访问级别 | 访问修饰控制符 | 同类 | 同包 | 子类 | 不同包 |
|---|---|---|---|---|---|
| 公开 | public | √ | √ | √ | √ |
| 受保护 | protected | √ | √ | √ | × |
| 默认 | 没有修饰符 | √ | √ | × | × |
| 私有 | private | √ | × | × | × |
注意事项:
- 修饰符可以用来修饰属性,成员方法,和类(只有public和默认可以修饰类);
- 只有默认和public可以修饰类,且遵循上表的访问权限特点;
- 暂时没有学习继承,关于子类的访问权限,在下文展开;
- 成员方法的访问规则和属性完全一致。
面向对象编程三大特征:
基本介绍:
- 封装;
- 继承;
- 多态。
封装(Encapsulation)——第一大特征:
封装介绍:
封装(encapsulation)就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作。
封装的理解和好处:
- 隐藏实现的细节;
- 可以对数据进行验证,保证安全合理性。
- 比如我们使用电脑,但是我们可以不用去知道电脑是怎么运行的。
封装的实现步骤:
- 属性私有化private;
- 提供一个public的set用于修改属性(Alt + I调出,选择Setter);
- 提供一个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 父类{}
- 子类就会自动拥有父类定义的属性和方法;
- 父类叫做超类,基类(共有属性和共有方法);
- 子类又叫派生类(特有属性和特有方法)。
快速入门:
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();}
}
继承带来的便利:
- 代码的复用性提高了;
- 代码的扩展性和维护性提高了。
继承的细节:
-
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问(可以间接访问),要通过父类提供的公共的方法;
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();} } -
子类必须调用父类的构造器,完成父类的初始化。
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用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();} } -
若希望指定去调用父类的某个构造器,则显示的调用一下: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();} } -
super在使用时,需要放在子类构造器的第一行,super关键字只能在构造器中使用;(先有爸爸,才能有儿子)
-
super()和this()(this()是调用同类中的其它构造器的意思)都只能放在构造器的第一行,且这两个方法不能共存一个构造器;
-
java所有类都是Object类的子类;
-
父类构造器的调用不限于直接父类!将一直往上追溯到Object类(顶级父类);
-
子类最多只能继承一个父类(指直接继承),即java中是单继承机制(想要A继承B和C只能是B继承C,然后A再继承B);
-
不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(Person is a Music)。
继承的本质:
(294/910)
当子类对象创建好后,建立查找关系。
super关键字:
super代表父类的引用,用于访问父类的属性、方法、构造器。
基本语法:
- 访问父类的属性,但不能访问父类的private属性:super.属性名;
- 访问父类的方法,不能访问父类的private方法:super.方法(参数列表);
- 访问父类的构造器:super(参数列表);只能放在构造器的第一句,只能出现一句。
super给编程带来的便利/细节:
- 调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化);
- 当子类中有和父类的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。若没有重名,使用super、this、直接访问时一样的效果;
- super访问不限于直接父类,若爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;若多个基类中都有重名的成员,使用super访问就近原则。
super和this的比较:
| No. | 区别点 | super | this |
|---|---|---|---|
| 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();}
}
注意事项和使用细节:
需要满足以下条件:
- 子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样;
- 子类方法的返回类型和父类方法返回类型一样,或者是父类发返回类型的子类,例如:父类 返回类型是Object,子类方法返回类型是String
- public Object getInfo();父类
- public String getInfo();子类
- 子类方法不能缩小父类方法的访问权限。
- void sayOk();
- 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);}
}
为了解决代码的复用性不高,且不利于代码维护,我们引入了多态的概念。
多态的基本介绍:
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立再封装和继承的基础上的。
多态的具体体现:
-
方法的多态(重写和重载体现多态);
-
对象的多态(核心,困难,重点):
- 一个对象的编译类型和运行类型可以不一致;
- 编译类型在定义对象时,就确定了,不能改变;
- 运行类型是可以变化的;
- 编译类型看定义时 = 号左边,运行类型看 = 右边;
Animal animal = new Dog();animal的编译类型时Animal,运行类型是Doganimal = 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());
// }
}
多态的注意事项和细节讨论:
多态的前提:两个对象存在继承关系(建立在继承和封装的基础上);
多态的向上转型。
- 本质:父类的引用指向了子类的对象;
- 语法:父类类型 引用名 = new 子类类型();
- 特点,编译类型看左边,运行类型看右边。
- 可以调用父类中的所有成员(需遵守访问权限,无法使用private);
- 不能调用子类中特有成员(因为在编译阶段,能调用哪些成员由编译类型决定);
- 最终运行结果看子类的具体实现(若子类没有,就找父类,和前面super的调用是一样的)。
多态的向下转型:
-
语法:子类类型 引用名 = (子类类型) 父类引用;
-
只能强转父类的引用,不能强转父类的对象;
Animal animal = new Cat(); // animal是父类的引用,指向的确实是Cat类型的对象。 Cat cat = (Cat) animal; -
要求父类的引用必须是指向当前目标类型的对象;(披着羊皮的狼是狼,不会是老虎。)
-
当向下转型后,可以调用子类类型中所有的成员。
属性的重写问题:
属性没有重写之说,属性的值看编译类型。
instanceof 比较操作符,用于判断对象的类型是否为XX类型或XX类型的子类型(判断的是运行类型),若是,返回true,否则为false。
动态绑定机制(非常非常重要):
- 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定;
- 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。
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的对比:
== 是一个比较运算符:
- 既可以判断基本类型,也可以判断引用类型是否相等;
- 判断基本类型时,判断的是值是否相等;
- 判断引用类型时,判断地址是否相等,即判断是不是同一个对象(只关注运行类型,不管编译类型)。
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类的一个方法:
-
只能用于判断引用类型;
-
默认判断的是地址是否相同,子类中往往重写该方法,用于判断内容是否相同。
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 提供的哈希表)的性能。
- 提高具有哈希结构的容器的效率;
- 两个引用,若指向同一个对象,则哈希值肯定是一样的;
- 两个引用,若指向的是不同的对象,则哈希值肯定是不一样的;
- 哈希值主要是根据地址号来的!不能完全将哈希值等价为地址;
- 以后在集合中,若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方法:
- 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作(比如,对象打开了一个文件,这就是占用了一个资源,关闭文件就是释放资源);
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁对象,正在销毁对象前,会先调用finalize方法;
- 垃圾回收机制的调用,是由系统来决定,也可以通过System.gc()主动出发垃圾回收机制。
- 在新版中,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 访问。
断点调试:
为什么要有断点调试?
断点调试能让我们一步步的看原码的执行过程,从而发现错误所在;
在断点调试的过程中,是运行状态,是以对象的运行类型来执行的。
断点调试介绍:
- 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住(该行此时没有执行),然后,你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下,进而分析从而找到bug所在。
- 是程序员必须掌握的技能;
- 断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的Java水平。
断点调试的快捷键:
- F7:跳入方法内;
- F8:逐行执行代码;
- shift + F8:跳出方法;
- 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的方法源码,需要先配置一下:(要是不想看就自己勾选回来吧~)
- 点击Setting --> Build,Execution,Deployment --> Debugger --> Stepping;
- 把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开发 零钱通项目,可以完成收益入账,消费,查看明细,退出系统等功能。
- 先完成菜单,并可以选择:
- 完成零钱通明细(即内部的功能)
- 完成收益入账;
- 完成消费;
- 完成退出功能;
项目开发说明:
- 项目代码实现:
- 先完成基本功能(过程编程);
- 后改进为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),这周状态有点小差,另外这章真的好多,学的比较慢。。。明天把作业写了~
相关文章:
08、Java学习-面向对象中级:
Java学习第十二天——面向对象中级: IDEA: 创建完新项目后,再src里面创建.java文件进行编写。 src——存放源码文件(.java文件);out——存放编译后的字节码文件(.class文件) 在I…...
springboot集成onlyoffice(部署+开发)
前言 最近有个项目需求是实现前端页面可以对word文档进行编辑,并且可以进行保存,于是一顿搜索,找到开源第三方onlyoffice,实际上onlyOffice有很多功能,例如文档转化、多人协同编辑文档、文档打印等,我们只用…...
LabVIEW编程基础教学(二)--数据类型
在LabVIEW中,数据类型是非常重要的基本概念,因为它们决定了如何存储和操作数据。掌握这些基础数据类型对于编写有效的程序非常关键。以下是LabVIEW中的基础数据类型介绍: 1. 数值类型(Numeric) 整型(Inte…...
「Mac畅玩鸿蒙与硬件29」UI互动应用篇6 - 多选问卷小应用
本篇将带你实现一个多选问卷小应用,用户可以勾选选项并点击提交按钮查看选择的结果。通过本教程,你将学习如何使用 Checkbox 组件、动态渲染列表、状态管理及用户交互,构建完整的应用程序。 关键词 UI互动应用Checkbox 组件状态管理动态列表…...
Flutter中文字体设置指南:打造个性化的应用体验
在使用Flutter进行开发时,可能会遇到中文字体显示不正常或者字体不符合设计需求的情况。Flutter默认的中文字体往往无法满足某些用户对个性化和美观的需求。今天,我们就来详细探讨如何在Flutter应用中设置中文字体,并结合不同场景提供相应的解…...
git下载慢下载不了?Git国内国外下载地址镜像,git安装视频教程
git安装下载的视频教程在这 3分钟完成git下载和安装,git国内外下载地址镜像,Windows为例_哔哩哔哩_bilibili 一、Git安装包国内和国外下载地址镜像 1.1国外官方下载地址 打开Git的官方网站:Git官网下载页面。在页面上选择对应的系统&…...
安卓属性动画插值器(Interpolator)详解
属性动画(Property Animation)是 Android 中一个强大的动画框架,允许开发者对视图的任意属性(如位置、透明度、尺寸、颜色等)进行平滑的动态变化。插值器(Interpolator)作为属性动画的一部分&am…...
OSPF总结
1.定义及相关信息 (1)全称:Open ShortestPath First,开放式最短路径优先 (2)是一种基于链路状态算法的路由协议 (3)目前针对IPv4协议使用的是OSPF Version2(RFC2328) 目前针对IPv6 协议使用的是 OSPF Version3 ( RFC2740 ) (4)运行 OSPF 路由器之间…...
Spring Boot驱动的多维分类知识管理系统
1 绪论 1.1 研究背景 在这个推荐个性化的时代,采用新技术开发一个多维分类的知识管理系统来分享和展示内容是一个永恒不变的需求。本次设计的多维分类的知识管理系统有管理员和用户两个角色。 管理员可以管理用户信息,知识分类,知识信息等&am…...
CSS教程(七)- 背景
介绍 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 1 背景颜色 属性名:background-color 作用:指定HTML元素的背景色。 取值:英文颜色、16进制、rgb、rgba、transparent(一般为透明&#…...
PNG图片批量压缩exe工具+功能纯净+不改变原始尺寸
小编最近有一篇png图片要批量压缩,大小都在5MB之上,在网上找了半天要么就是有广告,要么就是有毒,要么就是功能复杂,整的我心烦意乱。 于是我自己用python写了一个纯净工具,只能压缩png图片,没任…...
【双十一特惠】腾讯云省钱攻略:如何智取云计算资源
前言 双十一不仅是购物的狂欢节,对于云计算用户来说,更是一个节省成本的绝佳时机。腾讯云,作为国内领先的云计算服务商,每年双十一都会推出一系列优惠活动。本文将为您揭开如何在这个购物节中,最大化利用腾讯云的优惠…...
爬虫学习8
Frida是一个动态代码插桩工具,允许开发者在运行时修改和调试应用程序 import ...:这行代码表示导入所需的模块或库,但具体的导入内容在图片中被省略了。 rdev frida.get_remote_device():这行代码获取一个远程设备实例ÿ…...
双指针算法的妙用:提高代码效率的秘密(2)
双指针算法的妙用:提高代码效率的秘密(2) 前言: 小编在前几日讲述了有关双指针算法两道题目的讲解,今天小编继续进行有关双指针算法习题的讲解,老规矩,今天还是两道题目的讲解,希望…...
笔记--(网络3)、交换机、VLAN
交换机 交换机(Switch)意为“开关”是一种用于电(光)信号转发的网络设备。它可以为接入交换机的任意两个网络节点提供独享的电信号通路。最常见的交换机是以太网交换机。其他常见的还有电话语音交换机、光纤交换机等。 交换机的…...
昇思大模型平台打卡体验活动:基于MindSpore实现GPT1影评分类
如果你对MindSpore感兴趣,可以关注昇思MindSpore社区 大模型平台 平台说明 昇思大模型平台旨在为AI学习者和开发者提供在线学习的项目、模型、大模型体验和数据集的平台。我们也添加了各领域的经典数据集来帮助学习者解决AI学习过程中的一系列难题, 如…...
如何调整pdf的页面尺寸
用福昕阅读器打开pdf,进入打印页面,选择“属性”,在弹出的页面选择“高级” 选择你想调成的纸张尺寸,然后打印,打印出来的pdf就是调整尺寸后的pdf...
IDA*算法 Power Calculus————poj 3134
目录 闲聊 前言 DFS算法的无效搜索 BFS算法的空间浪费 IDDFS A*算法 IDA* Power Calculus 问题描述 输入 输出 问题分析 代码 闲聊 前几周在忙着数学竞赛,所以就没时间更新,高等数学,一生之敌,真不知道报名的时候我是怎么想…...
重磅!CoRL 2024顶刊会议 清华大学高阳研究组发布“基于大模型先验知识的强化学习”
正在德国举办的机器人研究领域的顶级学术会议CoRL 2024,清华大学交叉信息研究院高阳研究组发布重磅研究成果,提出“基于大模型先验知识的强化学习”框架(Reinforcement Learning with Foundation Priors) 来促进具身智能体在操作任务中的学习…...
泷羽sec学习打卡-Windows基础命令
声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 关于windows的那些事儿-Base 一、Windows-BaseWindows有哪些版本呢,有什么区别呢?…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
