成员变量和局部变量
成员变量 | 局部变量 | |
---|---|---|
声明位置 | 类中 | 方法中 |
作用域 | 类中 | 方法中 |
默认值 | 同数组默认值 | 无默认值,必须赋值才能使用 |
生命周期 | 随着对象存在、消失 (已初始化的成员变量先于类加载,未初始化的随类(在类后)加载) | 随着所属区域的执行存在,随区域结束而释放 |
存储位置 | 堆内存 | 栈内内存的方法中 |
- 调用变量名时, 成员变量和局部变量同名时,遵循就近原则;(离成员近,调的是成员;离局部近,调的是局部)
类与对象
创建对象
类名 实例名 = new 类名();
new关键字作用:在内存中,为 实例对象 申请开辟存储空间
public class Car {
public double price;
public String brand;
public String color;
public void run() {
System.out.println("价值"+price+"的"+color+"的"+brand+"跑了");
}
public void stop() {
System.out.println("价值"+price+"的"+color+"的"+brand+"停了");
}
}
public static void main(String[] args) {
Car cc=new Car();
cc.brand="保时捷";
cc.color="红色";
cc.price=1240000;
cc.stop();
}
- 主函数调用,栈中加载main函数(包含其中的内容)
- 主函数中顺序执行:
- car cc=new car();等号右边先执行,然后赋值给左边;
- 等号右边:实例化,在堆中申请内存,新建一个car类的对象 ,包含其属性、方法等信息;(此时属性是默认值)
- 通过等号赋值给左边;对象cc在栈中的main里,接收到新建的car的信息;(此时是默认值)
- 为cc的属性赋值,即将栈中的默认值替换为新值;cc引用指向栈中的内存地址,所以引用的属性值自然随之变化;
- 调用cc的run方法,run方法进入栈内存,执行功能:打印属性信息拼接的字符串;(引用的是堆中cc的属性值)
- car cc=new car();等号右边先执行,然后赋值给左边;
- 主函数结束,程序结束;
对象是引用类型
- 只要2个实例指向了一个地址,对任一实例操作任意属性,其他实例的属性也会变掉~
如下图,结果是t1和t2的name都是李四;
如下图,最终数组指向的是2个new出来的user()的地址值
构造函数
- 如果不声明构造函数
默认提供一个隐式的无参的构造函数;实例化对象时默认调用隐式无参构造函数; - 如果已经手动声明了有参的构造函数
则不再提供无参构造函数,如有需要需手动再声明无参构造函数(构造函数重载)才能使用无参的; - 构造函数无返回类型,连void都不要写!!
- 最先执行,优先于属性和方法的调用
- 自动生成构造函数:alt+shift+s —— generate constructor using fields
public class Teacher {
String name;
int age;
double height;
int salary;
public Teacher() {
System.out.println("默认无参构造函数");
}
public Teacher(String name,int age,double height,int salary) {
System.out.println("重载的有参构造函数执行");
this.age=age;
System.out.println(this.age);
this.name=name;
System.out.println(name);
this.height=height;
System.out.println(height);
this.salary=salary;
System.out.println(salary);
System.out.println("构造完毕");
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "[Teacher"+":"+"name="+name+" ; age="+age+" ; height="+height+" ; salary="+salary+"]";
}
}
public class Test1 {
public static void main(String[] args) {
System.out.println("main函数执行");//程序入口,最先执行
Teacher t1=new Teacher("小A", 38, 183, 200);//创建对象,调用有参构造函数,构造函数进栈;t1在堆中开辟内存,并将其属性赋的值存储下来,构造函数运行完毕
System.out.println(t1.name);
System.out.println(t1.age);
System.out.println(t1.height);
System.out.println(t1);//自动调用重写过的toString方法,打印出来的不是地址而是固定格式的字符串
Teacher t2=new Teacher();//创建第二个对象,调用无参构造函数
System.out.println(t2);//t2的属性未赋值,按重写过得toString打印默认值
System.out.println("main函数执行结束");//主函数结束
}
}
*输出依次为:
main函数执行
重载的有参构造函数执行
38
小A
183.0
200
构造完毕
小A
38
183.0
[Teacher:name=小A ; age=38 ; height=183.0 ; salary=200]
默认无参构造函数
[Teacher:name=null ; age=0 ; height=0.0 ; salary=0]
main函数执行结束
- 标准的实体类一般包含:属性(私有属性)、无参构造函数、重载构造函数、(get/set方法)
- this代表被创建出来的实例对象;(而不是类)
public static void main(String[] args) {
int i=3;
f(i);
System.out.println(i);//3
}
public static void f(int i) {
i=4;
System.out.println(i);//4
}
- i是基本数据类型,在栈中的方法体中,f方法中的i会随内存的释放而释放掉,故主方法中i仍只能取到原来的3;
public static void main(String[] args) {
int[] arr= {1,2,3,4};
f(arr);
System.out.println(arr[0]);//7
}
public static void f(int[] arr) {
arr[0]=7;
System.out.println(arr[0]);//7
}
- arr是数组,是引用数据类型,存放在堆中,不会随方法的释放而释放掉;f方法和主函数中对arr的操作都直接指向arr在堆中的地址,对其造成影响;
包
- 同一个包下的2个类互相使用,无需导包;
- 语言包(java.lang)中的类直接用,无需导入(比如String)
- *号是通配符,需要导入的2个类在同一个包中,直接 java.包名.*
- 自定义的类名尽量不要与jdk已有类名重复,否则使用jdk的类时需要使用完整包路径来使用;
静态 static
-
含义:共有的,共享数据
-
静态内容属于整个类而不属于某个对象,只要是该类的对象,都可以使用并操作该类的静态属性,且其值都是一致的;(即通过对象操作静态内容,会直接影响全局)
-
尽量使用类名调用静态变量,以便明示此时操作的是整个类的属性;
-
静态变量随着类加载,随类释放(生命周期长,占内存);
-
静态成员只能访问静态成员,不能访问非静态成员(也不能使用this)非静态成员可以访问静态成员也可以访问非静态
非静态成员,是在创建对象并调用后才进入内存; 静态成员在类加载时就进入内存; 故: 非静态成员加载的时候静态成员已经存在于内存,可以使用; 静态成员加载的时候非静态成员还不存在,无法调用; this也是一样,this指代的是当前对象,也要在创建对象后才能存在;
-
使用场景
- 工具类的静态常量和方法————以便直接通过类名调用(如Math.pow() Math.PI)
- 共享数据
封装
- 优点:提高安全性(避免静态公有属性被随意修改)、提高复用性(方便直接通过方法名、类名、接口名调用)
- 方式:提供私有化属性和get/set方法
public class Nurse {
private String name;
private String id;
private int age;
private String qualification;
public Nurse(){
}
public Nurse(String name,String id,int age,String qualification){
this.age=age;
this.id=id;
this.name=name;
this.qualification=qualification;
}
public void setAge(int age){
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getQualification() {
return qualification;
}
public void setQualification(String qualification) {
this.qualification = qualification;
}
}
一个完整的实体类需要具备:
- 私有属性;
- 无参构造函数;
- 全参构造函数;
- 私有属性的get/set方法
继承 extends
使用场景:类与类之间、接口与接口之间;
- 父类(超类、基类 super class)已有的非私有方法和属性,子类(派生类、衍生类 subclass)可以直接使用(通过 子类实例名.父类非私有属性/方法 );
优点:提高代码的复用性;多态的前提
public class A(){
int i =10;
int j =30;
}
public class B extends A(){
int i=20;
int k=super.i;//访问父类重名成员变量的方法1
public void f(){//访问父类重名成员变量的方法2
System.out.println(super.i);
return super.i;
}
}
public class Test1(){
System.out.println(b.j);//可以直接访问父类的非私有属性,结果30
System.out.println(b.i);//子类和父类的成员变量重名,优先调用到的是子类的20
System.out.println(super.i);//编译失败,Test1的父类是object,super指的是object,并没有i属性;
System.out.println(b.k);//访问父类重名成员变量的方法1:在子类中使用super访问到当前类的父类,并接受到10;
b.f();//访问父类重名成员变量的方法2:子类中定义方法并使用super进行访问,此处直接调方法(执行输出或者获取值都可以写在方法里);
}
public class TestSuper {
int i=100;
public void A() {
System.out.println(i);
}
public void Fu() {
System.out.println(i);
}
}
public class TestSub extends TestSuper{
int i =200;
public void A() {
System.out.println(i);
}
public void Zi() {
System.out.println(i);
}
}
public class SuperAndSub {
public static void main(String[] args) {
TestSub sub=new TestSub();
sub.A();//200,重名方法,是谁调用走谁的方法,取谁的变量
sub.Fu();//100,非重名方法,谁有这个方法走谁的方法,取谁的变量————此处是父类的方法
sub.Zi();//200,非重名方法,谁有这个方法走谁的方法,取谁的变量————此处是子类的方法
}
}
重写/复写/覆盖 | 重载 | |
---|---|---|
名称 | override | overload |
规则 | 方法名、参数列表和修饰符都相同,方法体不同 | 方法名相同,参数列表不同 |
使用情景 | 分别在子类和父类中的2个同名方法(只发生在继承关系中) | 发生在同一个方法中 |
意义 | 对方法功能的扩展 | 对类的灵活性的扩展 |
其他 | 子类功能更多,父类范围更广 | 重载的方法之间没有高低优劣之分 |
注意事项 | 不能为了使用某一类的方法,盲目使用继承(可以使用导入,new它的对象就好); 一个子类只能有一个父类,一个父类可以有多个子类(类只支持单继承,接口支持多继承); 不能环形继承,A继承B,B继承C,C继承A这种,NO!!! |
-
权限修饰符问题
- 父类方法使用public/protected/default,子类重写时只要大于等于父类的范围即可; - public>protected>default>private;父类要是private,子类无法继承也无法重写
-
重写时,先调用super.方法名();,可以方便地添加父类原有的所有功能,再在重写的代码块里写新增的功能;
@Override
public void A() {
super.A();
System.out.println(i+1);
}
继承中的构造函数
- 子类构造函数中只要不手动调用构造函数,第一行都默认调用父类空参构造函数,super(),可以不写(隐式默认调用)
- 如果父类属性私有,子类无法使用this关键字调用私有属性,故需要用父类属性的get/set方法给参数赋值,但这样太过麻烦。故可以在子类声明有参构造函数时调用父类的有参重载构造函数;
- 构造函数的调用super()或this()必须在第一行,所以不可能同时调用父类的有参和无参构造函数;不可能同时调用父类的构造函数和子类的构造函数(手动调用时,隐式调用父类构造函数的行为就默认取消了)
- 本类构造函数也可以重载,在其他本类构造函数的第一行调用
public class Fu {
private String name;
private String color;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Fu(String name, String color, int age) {
super();
this.name = name;
this.color = color;
this.age = age;
}
public Fu() {
super();
// TODO Auto-generated constructor stub
}
}
public class Zi extends Fu{
int height;
public Zi(String name, String color, int age, int height) {
super(name, color, age);
this.height = height;
}
}