本文为B站Java教学视频BV1Kb411W75N的相关笔记,主要用于个人记录与分享,如有错误欢迎留言指出。
本章笔记涵盖视频内容P174 ~ P365
声明: 权限修饰符 数据类型 变量名
class Test{ String name; }
对属性可以赋值的位置:
①默认初始化
②显式初始化 / ③在代码块中赋值
④构造器中初始化
⑤有了对象后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
//Test.java public class Test { public static void main(String[] args) { String name; //默认初始化 String name_a = "HI"; //显示初始化 String name_b; { name_b = "HII"; //在代码块中赋值 } Tests tests = new Tests(); tests.name_d = "HIIII"; //有了对象后,通过"对象.属性"或"对象.方法"的方式进行赋值 } } //Tests.java public class Tests { String name_c; String name_d; public Tests() { name_c = "HIII"; //构造器中初始化 } }
属性(成员变量) 与 局部变量的区别:
相同点:
不同点:
在类中声明的位置不同
属性:直接定义在类内
局部变量:声明在方法,方法形参,代码块,构造器形参,构造器内部
关于权限修饰符的不同
属性:可以在声明属性时,指明其权限,使用权限修饰符,如:private,public,protected,缺省
局部变量:不可以使用权限修饰符
默认初始化值的情况:
属性:类的属性,根据其类型,都有默认初始化值
整型(byte,short,int,long):0
浮点型(float,double):0.0
字符型(char):0
布尔型(boolean):false
引用数据类型(类,数组,接口):null
局部变量:没有默认初始化值,在调用之前必须要显式赋值。
特别的,形参在调用时才需要赋值
在内存中加载的位置:
属性:加载到堆空间中(非static)
局部变量:加载到栈空间
声明: 权限修饰符 当前类名 (形参列表) {}
//构造器可以创建并初始化对象的信息 class Test{ String name; //空参构造器 public Test (){} public Test (String name){ this.name = name; } }
注意事项:
声明:static(可选) {代码块}
//代码块可用来初始化类或对象,只能使用static或"同步"修饰 class Test{ String name; public Test (){} public Test (String name){ this.name = name; } { //代码块 } }
非静态代码块和静态代码块的比较
非静态代码块 | 静态代码块 |
---|---|
内部可以有输出语句 | 内部可以有输出语句 |
随着对象的创建而执行 | 随着类的加载而执行,而且只执行一次 |
作用:可以在创建对象时,对对象的属性等进行初始化 | 作用:初始化类的信息 |
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行 | 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行 |
每创建一个对象,就执行一次非静态代码块 | 静态代码块的执行要优先于非静态代码块的执行 |
非静态代码块内可以调用静态或非静态的属性,方法 | 静态代码块内只能调用静态的属性,静态的方法,不能调用非静态的结构 |
代码块更多的是和其它代码一起结合使用,用于界定不同代码之间的域。但是结合static或同步机制,代码块也可以"独立"发挥作用。
声明:权限修饰符 返回值类型 方法名 (形参列表) {方法体}
//使用方法可以调用当前类的属性和方法 class Tests{ private String name; public Tests (){ } public Tests (String name){ this.name = name; } { //代码块 } public void setName(String name){ this.name = name; } }
注意事项:
当方法A中又调用了方法A,称为递归
方法中不可以定义方法
方法形参在传参的时候,基本数据类型和String自身是不会因为方法内部值的改变而改变的。但是除了String的引用数据类型,由于传入的是地址值,所以方法内如果值发生改变,那么其本身值也会发生改变。
(Java的方法和C的函数类似)
方法的重载:
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可,与方法的权限修饰符,返回值类型,形参变量名,方法体均没有关系
void show(int a,char b,double c){} int show(int z,char x,double y){} //非重载 void show(int a,double c,char b){} //重载 int show(int a,double c,char b){} //重载
方法的重写:
public class ExtendsTest { public static void main(String[] args) { Son son = new Son(); Father father = new Father(); son.Talk(); //输出:我是子类10 father.Talk(); //输出:我是父类10 } } class Father{ public int a = 10; public void Talk(){ System.out.println("我是父类" + a); } } class Son extends Father{ public void Talk(){ System.out.println("我是子类" + a); } }
可变个数形参:
//格式:数据类型...变量名 public void show(String ... strs){ System.out.println("HERE!"); } //当调用可变个数形参的方法时,传入的参数个数可以是0个,1个或多个 test.show("hello"); test.show("hello","world"); //可变个数形参的方法若与本类中方法名相同,形参不同的方法之间构成重载,且优先级比较低 public void show(String strs){ System.out.println("HERE!"); } //可变个数形参在方法的形参中,必须声明在末尾;最多只能声明一个可变形参 public void show(int n,String ... strs){ System.out.println("HERE!"); } //若要调用传入的参数,则参照数组使用下标的方式调用 public void show(String ... strs){ for(int i = 0;i < strs.length;i++){ System.out.println(strs[i]); } } /* 实际上多个形参的方法本质上使用的是数组的方式传参,相当于是public void show(String[] strs),只不过省去了创建数组挨个赋值的麻烦。注意public void show(String[] strs)是无法与public void show(String...strs)重载的,两者会互相冲突 */
关于"多态性编译看左边,运行看右边"的理解
//对于展现多态性的代码(如下),在编译的时候只会看父类(左边)的代码,在运行的时候才会看右边(子类)的代码 Animal p = new Cat(); //这里Cat是Animal的子类 //而对于非多态性的代码,在编译和运行的时候都只会看左边的代码 Aniaml p = new Animal();
定义:Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
成员内部类(静态,非静态):
//实例化成员内部类的对象 public class Test { public static void main(String[] args) { //创建Dog实例(静态的成员内部类),由于是静态内部类所以可以直接实例化 Person.Dog dog = new Person.Dog(); dog.show(); //创建Bird实例(非静态的成员内部类),由于是非静态内部类所以必须要先实例化外部类才能实例化 Person person = new Person(); Person.bird bird = person.new bird(); //主要此处实例化对象和一般的不一样 } } class Person { static class Dog{ public void show(){ System.out.println("I'm a dog!"); } } class bird{ public void show(){ System.out.println("I'm a bird!"); } } }
局部内部类(方法内,代码块内,构造器内):
class MyComparable implements Comparable{ public int compareTo(Object o){ return 0; } } return new MyComparable; //和匿名实现类类似
Object类
所有的java类都直接或间接的继承于java.lang.Object类。这意味着所有的java类具有java.lang.Object类声明的功能。
"=="和equals()
可以使用在基本数据类型变量和引用数据类型变量中
如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不要求类型相同)
如果比较的是引用数据类型变量:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
equals()的使用
包装类(Wrapper)
针对八种基本数据类型定义相应的引用类型,有了类的特点就可以调用类的方法
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Interger |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
基本数据类型,String类型和包装类之间的转化
基本数据类型 → 包装类:调用包装类的构造器
Integer in1 = new Integer(num1);
Interger in2 = new Interger("123");
不符合格式的输入会报错;特别的,对于Boolean类型,接收字符串转化的时候,只要不是"true",都会默认为false
这些包装类的初始化默认值也发生了改变,由于是类,初始值统一为null
包装类 → 基本数据类型:调用包装类的xxxValue()
int i1 = in1.intValue();
float f2 = f1.floatValue();
自动拆箱/自动装箱:
int num2 = 10; boolean b1 = true; //"手动"装箱: Integer in1 = new Integer(num1); Boolean b2 = new Boolean(b1); //自动装箱:基本数据类型 → 包装类 Integer in1 = num1; Boolean b2 = b1; //"手动"拆箱 int num3 = in1.intValue(); //自动拆箱:包装类 → 基本数据类型 int num3 = in1;
基本数据类型,包装类 → String类型
连接运算
String str1 = num1 + "";
调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);
String类型 → 基本数据类型,包装类:调用包装类的parseXxx(String s)
int num2 = Interger.parseInt("123");
不符合格式的输入会报错;特别的,对于Boolean类型,接收字符串转化的时候,只要不是"true",都会默认为false
定义:在实际问题中,往往需要给属性赋值加入额外的限制条件。这个条件无法在属性声明时体现,所以只能通过方法进行限制条件的添加。因此,为了避免用户使用"对象.属性"的方式直接对属性进行赋值,需要将属性声明为私有的(private)。此时,针对于属性就体现了封装性。
一般将类的属性Xxx私有化(private),同时提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | Yes | |||
缺省(default) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
Java规定的四种权限:private < 缺省 < protected < public
四种权限都可以用来修饰类的内部结构:属性,方法,构造器,内部类;若要修饰类,只能使用缺省和public
(跨包使用的话要导包import)
格式:class A extends B{}
A:子类,派生类,subclass
B:父类,超类,基类,superclass
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法。特别的,父类中声明为private的属性或方法,子类继承父类之后,仍然获取了父类中私有的结构。只是因为封装性的影响,子类不能直接在类内调用父类的结构。
子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
Java中关于继承性的规定:
多态性是一个比较抽象的性质,可以理解为父类的引用指向子类的对象
有了对象的多态性后,在编译期只能调用父类中声明的方法,但是在运行期,实际执行的是子类重写父类后的方法。(编译看左边,运行看右边)
多态性的使用前提:①类的继承关系 ②方法的重写 (对象的多态性只适用于方法,不适用于属性)
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法。
使用强制类型转换符(向下转型)就可以使具有多态性的对象调用子类中特有的属性和方法
//举例,若Man为Person的子类,则下方程序创建了一个具有多态性的对象,此对象无法调用Man中特有的方法 Person p1 = new Man(); //下方程序使用强制类型转换符,此时m1可以使用Man中特有的方法 Man m1 = (Man)p1; m1.earnMoney(); //使用强转时,可能会出现ClassCastException的异常 Woman w1 = (Woman)p1; w1.goShopping(); //创建多态对象的时候,p1使用的是Man类,所以即便强转也不能强转为与Man同级的Woman
匿名对象:创建的对象,没有显式的赋给一个变量名,即为匿名对象
特征:匿名对象只能调用一次
使用:如,mall.show(new Phone()); 直接创建一个对象给方法使用
定义:this可以理解为当前对象,或当前正在创建的对象;this可用来修饰属性,方法,构造器
应用场景:
this指定变量
在类的方法或构造器中,可以使用"this.属性"或"this.方法"的方式,调用当前对象的属性或方法,但是一般情况下都会省略"this."。特别的,当方法的形参和类的属性同名时,必须显式的使用“this.变量”的方式,表明此变量是属性而非形参。
class Person{ String name; int age; //被隐藏的this (this.name = n;) public void setName(String n){ name = n; } //必须要添加的this public void setAge(int age){ this.age = age; } }
this调用构造器
public Person(int age){ this.age = age; } public Person(String name,int age){ this(age); //相当于当前的Person调用的上方Person构造器,复用了上方构造器的代码,简化了代码量 this.name = name; }
在类内快速调用自身
public class Girl{ String name; int age; public Girl(String name, int age){ this.name = name; this.age = age; } public String getName(){ return name; } public void setName(String name){ this.name = name; //以上this的使用都是1.的场景 } //****************************************** public int compare(Girl girl){ return this.age - girl.age; } //****************************************** /* 此处的this指的是调用者,比如A.compare(B),那么A就是类内的this,而B就是形参。这样做的好处就是省去了在形参内再写一次A的麻烦,让程序更加直观且简洁 */ }
包的概念可以更好的实现项目的管理
使用package声明类或接口所属的包,声明在源文件的首行
每"."一次,就代表一层文件目录
同一个包下,不能命名同名的接口,类
不同的包下,可以命名同名的接口,类
在源文件中使用import显式导入指定包下的类或接口,声明在包的声明和类的声明之间。
如果需要导入多个类或接口,那么可以并列显式导入多个import,或者使用譬如"java.util.*"的写法一次性导入util包下所有的类和接口。
如果导入的类或接口是java.lang包下的(如System),或者是当前包下的,则可以省略此import语句
如果在代码中使用不同包下同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
定义:super可以用来调用父类中的属性,方法,构造器
应用场景:
super调用属性和方法:
super调用构造器:
可以在子类的构造器中显式的使用"super (形参列表)"的方式,调用父类中指定的构造器
"super (形参列表)"的使用,必须声明在子类构造器的首行
在类的构造器中,对于"this (形参列表)"和"super (形参列表)"只能二选一,不能同时出现
在构造器的首行,若没有显式的声明"this (形参列表)"或"super (形参列表)",则默认调用的是父类中空参的构造器
在类的多个构造器中,至少有一个类的构造器使用了"super (形参列表)",调用父类中的构造器
public class CheckAccount extends Account{ private double overdraft; public CheckAccount(int id,double balance,double AIR,double overdraft){ super(id,balance,AIR); this.overdraft = overdraft; } //此处直接调用父类的构造器进行初始化,而父类中没有的overdraft就要自己初始化 }
super在使用上和this关键字有点像
定义:static可以用来修饰属性,方法,代码块,内部类
使用static修饰的属性,称为静态变量(类变量)
使用static修饰的方法,称为静态方法
随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
静态方法中,只能调用静态的方法或属性
非静态方法中,既可以调用非静态的方法和属性,也可以调用静态的方法和属性
应用场景:
注意事项:
public class Account{ private int id; private String pwd = "000000"; private static double interestRate; private static double minMiney = 1.0; private static int init = 1001; //对于常量,以及需要长久记录并公用的变量,可以声明为static类 public Account(){ id = init++; //由于init不会被重置,所以每一次创建Account,id都会是上一个的id+1 } public static double getInterestRate(){ return interestRate; } //static的变量,返回的方法也必须要是static的,反之同理 }
定义:final可以用来修饰类,方法,变量 (有些类似于C的const)
final 用来修饰类:表明此类不能被其他类所继承。
如:String类,System类等
final 用来修饰方法:表明此方法不能被重写
如:Object类中的getClass()
final用来修饰变量:此时的"变量"就称为是一个常量
final修饰属性:可以考虑赋值的位置有:显式初始化,代码块初始化,构造器初始化
final修饰局部变量:使用final修饰形参时,表明此形参是一个常量。当调用此方法时,给常量形参一个实参,一旦赋值以后,就只能在方法体内使用此形参,不能重新赋值
static final可以用来修饰属性或方法,修饰属性时属性称为全局常量
定义:abstract可以用来修饰的结构,类,方法
abstract修饰类:抽象类
abstract修饰方法:抽象方法
抽象方法只有方法的声明,没有方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法。
若子类重写了父类中所有的抽象方法,此子类可以实例化
若子类没有重写父类中所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
//抽象类(无法被创建对象) abstract class Employee{ String name; int id; int salary; public Employee() { } public Employee(String name, int id, int salary) { this.name = name; this.id = id; this.salary = salary; } abstract public void work(); //抽象方法(没有方法体) }
注意事项:
抽象类的匿名子类
//非匿名类,非匿名对象 Worker worker = new Worker(); method1(worker); //非匿名类,匿名对象 method1(new Worker()); //匿名类,非匿名对象 Person p = new Person(){ public void eat{ System.out.println("吃东西"); } }; method1(p); //匿名类,匿名对象 method1(new Person(){ public void eat{ System.out.println("吃东西"); } });
interface Flyable{ //全局常量 public static final int MAX_SPEED = 7900; int MIN_SPEED = 1; //省略前缀 //抽象方法 public abstract void fly(); void stop(); } interface Attackable{ //... } class Bullet implements Flyable,Attackable{ //重写方法 }
注意事项:
接口中不能定义构造器,这意味着接口不可实例化
Java开发中,接口通过让类去实现(implements)的方式来使用
如果实现类实现了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有实现接口中所有抽象方法,则此实现类仍为一个接口
接口与接口之间可以继承,而且可以多继承
接口中定义的静态方法,只能通过接口来调用
通过实现类的对象,可以调用接口中的默认方法;如果实现类重写了接口中的默认方法,调用时,调用的是重写以后的方法
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的方法
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下会报错
在子类(或实现类)的方法中调用父类,接口中被重写的方法
public void myMethod(){ method3();//调用自己定义的重写的方法 super.method3();//调用的是父类中声明的方法 CompareA.super.method3();//调用接口中的默认方法 }
接口的匿名实现类:
//USB是接口,Flash是USB接口的实现类 Computer com = new Computer(); //1.接口的非匿名实现类的非匿名对象 Flash flash = new Flash(); com.transferData(flash); //2.接口的非匿名实现类的匿名对象 com.transferData(new Flash()); //3.接口的匿名实现类的非匿名对象 USB phone = new USB(){ public void start(){ //重写USB接口中的方法 } public void stop(){ //... } }; com.transferData(phone); //4.接口的匿名实现类的匿名对象 com.transferData(new USB(){ public void start(){ //... } public void stop(){ //... } });
模型层 | model(主要处理数据) |
---|---|
数据对象封装 | model.bean/domain |
数据库操作类 | model.dao |
数据库 | model.db |
控制层 | controller(处理业务逻辑) |
---|---|
应用界面相关 | controller.activity |
存放fragment | controller.fragment |
显示列表的适配器 | controller.adapter |
服务相关 | controller.service |
抽取的基类 | controller.base |
视图层 | view(显示数据) |
---|---|
相关工具类 | view.utils |
自定义view | view.ui |
定义:单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例
饿汉式:声明类对象后立刻初始化,实例在程序运行中长久存在
class Bank{ //私有化类的构造器 private Bank(){} //内部创建类的对象,要求此对象也是静态的 private static Bank instance = new Bank(); //提供公共的静态的方法,返回类的对象 public static Bank getInstance(){ return instance; } }
懒汉式(不完善):声明类对象后先不初始化,等要使用的时候再初始化
class Bank{ //私有化类的构造器 private Bank(){} //声明当前类对象,没有初始化,要求此对象是静态的 private static Bank instance = null; //提供公共的静态的方法,创建实例并返回类的对象 public static Bank getInstance(){ if(instance == null){ instance = new Order(); } return instance; } }
饿汉式和懒汉式的优缺点对比:
饿汉式 | 懒汉式 | |
---|---|---|
优点 | 是线程安全的 | 延迟对象的创建 |
缺点 | 对象加载(存在)时间过长 | "目前"线程不安全 |
定义:在开发中实现一个算法时,整体步骤很固定,通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现,这就是模板模式。
//计算某段代码执行所需要的时间 abstract class Template{ public void spendTime(){ long start = System.currentTimeMillis(); code();//不确定的部分,易变的部分 long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); } public abstract void code; }