类、对象、引用
类
- 类是具有相同属性和行为的对象的集合。
- 类定义了该类型对象的数据结构, 称之为“成员变量”,同时也定义了一些可以被调用的功能,称之为“方法”。
- 类是用于构建对象的模板,对象的实质就是内存中的一块存储区域,其数据结构由定义它的类来决定。
- 定义类
- 类的成员变量
- 类的成员方法
对象
- 当一个类的定义存在后, 可以使用 new 运算符创建该类的对象。
- 对象创建的过程一般称为类的实例化
- 语法规则:
成员变量的初始化
- 对象创建之后,其成员变量可以按照默认的方式初始化,对象成员具有默认值。
- 成员变量的默认初始化值规则
成员变量类型 | 默认初始值 |
数值类型 byte、short、int、long、float、 | 0 |
boolean类型 | false |
char | 0000 |
引用类型 | null |
成员变量的调用
成员变量的调用,可以根据某个对象的引用找到成员变量,然后使用成员变量
//1.创建一个汽车
Car car = new Car();//创建对象
//2.给汽车属性赋值
car.pinpai="红旗";
car.color="红色";
car.fadongji="红旗发动机";
car.count=5;
成员方法的调用
- 方法的调用必须通过某个对象的引用。
- 当通过么对象的引用调用方法时,方法中涉及的成员变量就是该对象的成员变量。
引用类型
- 除8种基本数据类型之前,用类名(接口,数组)声明的变量称为引用类型变量,简称引用。
- 引用类型变量中存储的是某个对象在内存中的地址信息。
- 引用的功能在于访问对象。
- 引用类型变量声明语法规则:
JVM内存结构
方法区
- 该区间用于存放类的信息。java程序运行时候,首先会通过类加载器载入类文件的字节码文件,经 过解析后将其装入方法区。类的各种信息都在方法区保存。
栈内存区
- 栈用于存放程序运行过程中的局部变量。
- 引用类型的变量p ,存储在栈内存中。
堆内存区
- jvm会在其内存空间开一个称为“堆”的存储空间,这部分空间用于存储使用new关键字创建的对象。
- 创建了一个Person对象,存储在堆内存中。
方法的重载
- 在java语言中,允许多个方法的名称相同,但参数列表不同,称之为方法的重载(overload)。 编译器在编译时,会根据其参数的不同,绑定到不同的方法。
this
- 在方法中可以通过this关键字表示调用该方法的对象。
- 通常在类中使用this区分成员变量和参数,如果没有歧义,可以省略this。
- 一个类可以创建多个对象(存储于堆中),但方法只有一份(存储于方法区中)。
null
- 引用类型变量用于存放对象的地址,可以给引用类型赋值为null,表示不指向任何对象。
- 如果某个引用类型变量为null的时候,不能访问对象,否则抛出空指针异常。
构造函数
定义构造方法
- 构造方法是在类中定义的方法(每个类都有构造方法):
— 构造方法的名称必须和类名相同.
— 构造方法没有返回值,但也不能写void.
- 在java语言中可以通过构造方法实现对象成员变量初始化。
构造方法重载
- 为了使用方便对一个类定义多个构造方法,这些构造方法彼此参数不同,称为构造方法的重载。
- 创建对象时,java编译器会根据不同的参数,选择不同的构造函数。
类的继承
- extends关键字可以实现类的继承
- 子类(sub class) 可以继承父类(super class)的成员变量及成员方法。同时也可以定义自己的 成员变量和成员方法。
- java语言不支持多继承,一个类只能继承一个父类。 但一个父类可以有多个子类。
package oopday2;
import java.util.Date;
//动物类型:公共的成员变量, 公共的成员方法
// 颜色、生日、性别、脚的数量 叫、吃、跑
//鸡类:飞,
//狗:看门、
//** Java规定一个Java文件只能有一个公开类,并且这个公开类必须和Java文件名相同
public class Animal {// Animal:动物
//公共的成员变量
String color;//颜色
Date birth;//生日
String sex ;//性别
int count;//脚的数量
//公共成员方法
public void sing(String sing){
System.out.println("叫:" + sing);
}
public void eat(String food){
System.out.println("吃:" + food);
}
public void run(){
System.out.println("到处跑");
}
}
/*
Chicken这个类型只有一个成员方法
class Chicken{// Chicken:鸡
public void fly(){
System.out.println("飞....");
}
}*/
//鸡
//继承了Animal中所有成员方法和成员变量,同时有自己独有的fly方法
class Chicken extends Animal{
public void fly(){
System.out.println("飞....");
}
}
//狗
//继承了Animal中所有成员方法和成员变量,同时有自己独有的seeDoor方法
class Dog extends Animal{
public void seeDoor(){
System.out.println("保护家园....");
}
}
向上造型
- 一个子类的对象可以向上造型为父类的类型 Father f = new Sun();
- java编译器编译的时候根据父类型检查调用的成员变量和成员方法是否匹配。
/向上造型:子类对象赋值给父类的引用
Animal dog = new Dog();//
dog.birth = new Date();
dog.color = "黑色";
dog.sex = "男";
dog.count =4;
dog.sing("汪汪汪汪...");
dog.eat("骨头");
dog.run();
//dog.seeDoor();//编译错误:在编译的时候,dog引用是直接按引用的类型Animal使用,Animal没有seeDoor的方法
//在运行的时候,dog引用找到的是对象的实际类型Dog,进行使用,这个时候就有seeDoor方法
//如果一定要调用seeDoor(),这里只能修改dog的引用类型为Dog,即:Dog dog= new Dog();
instanceof关键字
- 对于一个父类的引用类型,可以指向该类的对象也可以指向其任意一个子类型的对象。
- 通过instanceof关键字判断引用指向的对象的实际类型。
- 根据引用指向的实际类型,将引用强制转换为实际类型
//4.创建Animal:动物就是动物,鸡和狗也是动物
Animal a1 = new Animal();
Animal a2 = new Dog();//Dog和Chicken都是Animal的子类
Animal a3 = new Chicken();
//5.类型转换:
//instanceof:用于判断引用对应的对象是否为某种类型,是结果为true,否则结果为false
//a2是动物,但a2本质上市狗,那么a2就不能转换为鸡这个类型
if (a2 instanceof Dog){ // instanceof的运算结果是ture或者false
//a2是Dog,那么就可以把a2转换为Dog,然后赋值给Dog类型的引用
Dog dog1= (Dog) a2;
dog1.seeDoor();//类型转换之后,就可以调用到子类Dog的所有方法
}
if (a3 instanceof Chicken){
Chicken chicken = (Chicken) a3;
chicken.fly();
}
//Chicken chicken = (Chicken) a2;//这里没有编译错误,有运行错误:ClassCastException - 类型转换错误
/*Exception in thread "main" java.lang.ClassCastException: oopday2.Dog cannot be cast to oopday2.Chicken
at oopday2.TestAnimal.main(TestAnimal.java:58)*/
}
继承中的构造方法
- 子类的构造方法中必须通过super调用父类的构造方法。因为创建子类之前,必须先创建父类。
- 子类的构造函数如果没有直接使用super调用父类构造方法,java编译器会自动的加入对父类无参构造函数的调用(那么要求父类必须有无参构造方法)。
package oopday2;
//图形
public class Shape {//Shape父类
int x;
int y;
public Shape(){//无参数构造函数
System.out.println("Shape的无参构造函数被调用");
}
public Shape(int x,int y){
this.x=x;
this.y=y;
System.out.println("Shape的有参构造函数被调用");
}
//成员函数
public double area(){
System.out.println("Shape的area函数被调用");
return 0;
}
}
class Rect extends Shape{//Rect:子类
double w;
double h;
public Rect(){
super();//调用父亲的无参构造函数,如果要写只能写在构造器函数内部的第一行
//super(); -- 构造器函数内部,会调用父亲型的构造函数,如果没有写super();那么编译器会自动加上super()
System.out.println("Rect的无参构造函数被调用了");
}
public Rect(int x,int y){
super(x,y);//这里表示调用父亲类型的有两个参数的构造器函数
System.out.println("Rect的两个有参构造函数被调用了");
}
public Rect(int x,int y,double w,double h){
//第一行就隐藏了super()
this.x=x;
this.y=y;//this.x=x; this.y=y; 这两句可以简化为super(s,y)
this.w=w;
this.h=h;
System.out.println("Rect的四个有参构造函数被调用了");
}
public Rect(double w,double h){
//super(w,h);//编译错误:super调用父类的构造器,必须是一个父类存在的
}
//重写:方法签名(方法的修饰符、返回值类型、方法名、参数)和父类的方法完全一致
public double area(){
System.out.println("Rect中的area函数被调用");
return w*h;
}
public double area(double w, double h){
return w*h;
}
}
方法重写(overwrite)
- 子类从父类继承的方法,如果不能满足子类的需要,可以重写父类的方法。即方法名和参数列表和父类保持完全一致,方法的实现不同。
- 子类重写父类的方法之后,子类对象优先调用自己的方法。
- 子类重写父类的方法的时候,可以在重写的方法中,使用super关键字调用到父类的方法。
//图形
public class Shape {//Shape父类
int x;
int y;
public Shape(){//无参数构造函数
System.out.println("Shape的无参构造函数被调用");
}
public Shape(int x,int y){
this.x=x;
this.y=y;
System.out.println("Shape的有参构造函数被调用");
}
//成员函数
public double area(){
System.out.println("Shape的area函数被调用");
return 0;
}
}
class Rect extends Shape{//Rect:子类
double w;
double h;
public Rect(){
super();//调用父亲的无参构造函数,如果要写只能写在构造器函数内部的第一行
//super(); -- 构造器函数内部,会调用父亲型的构造函数,如果没有写super();那么编译器会自动加上super()
System.out.println("Rect的无参构造函数被调用了");
}
public Rect(int x,int y){
super(x,y);//这里表示调用父亲类型的有两个参数的构造器函数
System.out.println("Rect的两个有参构造函数被调用了");
}
public Rect(int x,int y,double w,double h){
//第一行就隐藏了super()
this.x=x;
this.y=y;//this.x=x; this.y=y; 这两句可以简化为super(s,y)
this.w=w;
this.h=h;
System.out.println("Rect的四个有参构造函数被调用了");
}
public Rect(double w,double h){
//super(w,h);//编译错误:super调用父类的构造器,必须是一个父类存在的
}
//重写:方法签名(方法的修饰符、返回值类型、方法名、参数)和父类的方法完全一致
public double area(){
System.out.println("Rect中的area函数被调用");
return w*h;
}
public double area(double w, double h){
return w*h;
}
}
方法重载之后,子类调用自己重写之后的方法
package oopday2;
public class TestShape {
public static void main(String[] args) {
//1.创建一个矩形对象
//先创建父类对象,然后再创建子类对象
Rect rect = new Rect();//通过super(),调用父类的无参构造函数
//通过super(x,y),调用父类的无参构造函数
Rect rect1 = new Rect(3,4);
//通过super(),调用父类的无参构造函数
Rect rect2 = new Rect(3,4,10,10);
//调用成员方法
double res = rect2.area();
System.out.println("res:" + res);
Shape s=new Shape();
s.area();//s指向的是Shape对象,调用Shape的area方法
Shape s1=new Rect();
s1.area();//编译期s1是Shape,Shape中有area,所以没有编译错误
//运行的时候s1是Rect类型,矩形有自己的area方法
//所以调用Rect的area方法
}
}
package关键字
- 定义的时候需要指定类的名称,但是如果仅仅将类名作为类的唯一标志,则会出现命名冲突的问题
- 在Java中,是用package来解决命名冲突的问题,因此定义类的时候,一般需要先给类指定一个包名
- 类的真实名字:包名+类名。
- 包名可以由层次结构,一个类可以由多层包名
- 如果各个公司和组织的开发者都随意命名包,并不能很好的解决命名冲突的问题,因此指定包名时建议使用:公司域名反写+项目名+项目模块名+mvc模式分层
import关键字
- 如果要在程序中,使用某个类,可以用该类的全名,这样比较复制。
- 一般使用import 语句导入这个类,然后使用该类。
package oopday2;
import java.util.Date;
public class InportDemo {
public static void main(String[] args) {
//java.util.Date date = new java.util.Date();//Java.util.Date是Date类的全名
Date date = new Date();//使用了import关键字,先导入Java.util.Date,在类中就可以直接通过类名使用到这个类型
}
}
访问控制符
- private修饰的成员变量和方法仅仅只能在本类中调用,因此private修饰的内容是对内实现数据的 封装,如果“公开”会增加维护的成本。
- public修饰的成员变量和方法可以在任何地方调用,因此public修饰的内容是对外可以被调用的功能。
- 在定义类的时候,一般采用成员变量私有化,方法公开的原则。
- 用protected修饰的成员变量和方法可以被子类及同一个包中的类使用。
- 默认访问控制即不书写任何访问控制符,默认访问控制的成员变量和方法可以被同一个包中的类调用
修饰符 | 本类 | 同一个包中的类 | 子类 | 其他类 |
public | 可以访问 | 可以访问 | 可以访问 | 可以访问 |
protected | 可以访问 | 可以访问 | 可以访问 | 不能访问 |
默认 | 可以访问 | 可以访问 | 不能访问 | 不能访问 |
private | 可以访问 | 不能访问 | 不能访问 | 不能访问 |
对于类的修饰可以使用public和默认方式,public修饰的类可以被任何一个类使用,默认访问控制的类只可以被同一个包中的类使用。protected和private可以用于修饰内部类 |
package oopday2;
public class Cat {
String name; // 没有public private protected的修饰,所以就是默认的访问权限
String sex;
//访问权限:public > protected >默认 > private
public void catchMouse(){
System.out.println(name + "正在抓老鼠");
}
protected void eat(String food){
System.out.println(name + "吃:" + food);
}
void play(){
System.out.println(name + "陪你玩....");
}
private void sing(){
System.out.println("喵喵...");
}
}
return关键字:Java中方法的定义
- java中规定方法必须要指定返回值类型,如果没有返回值类型,可以使用void关键字。
- java中使用return关键字,在方法的内部返回数据。
- 定义方法的语法规则 :方法的修饰符 方法的返回值 方法的名字(方法的参数){ 方法体 }
- 代码
package oopday3;
// 方法的定义
// *** ① 方法的参数有哪些,什么类型的 ② 方法是否有返回值,如果有是什么类型的
// ③ 方法取一个什么名字(见名知意) ④ 方法的修饰符
// *** return 用于方法中返回一个数据。 return之后的代码都不能被执行。
// 有时候用return 结束方法的运行。
public class MethodDemo {
// 1. 计算矩形的周长,并返回这个数据。 【方法的参数两个,double , 方法有返回值,double 】
public double girth(double w , double h){
return 2 * ( w + h) ;//
}
// 2. 输出矩形的周长。 【方法有两个参数 ,double, 无返回值】
public void girth1(double w , double h){
System.out.println("周长:" + 2*(w+h));
}
// static : 静态的
public static void main(String[] args) {
// 0. 创建对象
MethodDemo demo = new MethodDemo();
// 1. 调用girth
demo.girth(10,10); // 静态方法内, 通过对象调用对象非静态方法。
// 2. 调用girth1.
demo.girth1(10,1);
}
}
static关键字
修饰成员变量
- 用static修饰的成员变量不属于对象的数据结构
- static修饰的变量属于类的变量,通常可以通过类名来引用static成员
- static成员变量和类的信息一起存储在方法区,而不是在堆内存中
- 一个类的静态成员变量只有一份,而非静态成员对象有多份,即每个对象都有自己的非静态成员变量
修饰成员方法
- 类中的方法,通常都会涉及到堆具体对象的操作,这些方法在调用时,需要隐式的传递对象的引用。
- static修饰的方法则不需要针对对象进行操作,其运行结构仅仅与输入的参数有关系。调用时候直接通过类名引用。
- static修饰的方法是属于类的方法,通常用于提供工厂方法,或者工具方法。
- static修饰的方法,调用的时候没有具体的对象,因此static修饰的方法不能调用非静态成员变量和成员方法
static的使用
package oopday3;
// Cat类型
public class Cat {
// 非静态的成员变量,属于对象。必须要先创建对象,才可以调用这个成员变量。
String name ;
int age ;
// 静态的成员变量,属于类 ,可以通过类名调用静态成员变量。
static int number ; // 代表总共有多少只猫。
public Cat(){
number ++ ;//new 一个猫,就增加一次。
}
public Cat(String name , int age){
this.name = name ;
this.age = age ;
number ++ ;// new 一个猫,就增加一次。
}
public static void print(){ // 静态方法: 属于类, 通过类名直接调用静态方法. Cat.print() , 这个调用的时候没有对象。
// 方法的内部就不能this。 因为this是指代调用这个方法的对象。
// System.out.println(this.name +":" + this.age); 如果就是需要输出对象相关的内容(非静态成员变量) ,那么就只能用非静态的成员方法。
System.out.println("当前总共有:" + number + "只猫");
// sing();// 编译错误: 非静态的方法,不能再静态代码块中使用。
}
public void sing(){ // 非静态的方法,只能通过对象调用这个方法, 就可以使用对象的成员变量。
System.out.println(this.name +"喵喵.........");
print(); // 可以的: print方法属于类,可以通过对象调用。
}
public static void main(String[] args) {
Cat c1 = new Cat("小红" , 3);
c1.print();
Cat.number = 100 ;// 通过类名访问静态成员变量。
c1.print();
Cat c2 = new Cat("rose" , 2); //number 由100变为101.
c2.print();
Cat.number = 50 ;
c1.print();
}
}
final关键字
修饰类
- final关键字的类不可以被继承
- 对于影响类,设置了final修饰,可以避免滥用继承造成的危害
修饰方法
- final关键字修饰的方法可以不被重写
- 防止子类定义自己的方法的时候,不经意被重写
修饰成员变量
- final修饰成员变量,不可以改变
- 该成员变量需要在初始化的时候赋值,对象一旦创建,即改值不可以改变
- 代码
package oopday3;
// 测试java中的关键字final : final - 最终 ,修饰类不能被继承,修饰方法不能被重写,修饰成员变量,赋值之后,值不可变。
public class FinalDemo {
public static void main(String[] args) {
Coo coo = new Coo() ;//
coo.print(); // 调用的是父类中的print方法。
int age = 18 ;
// age是否为一个合法的年龄数据
if(age >= Coo.MIN_AGE && age <= Coo.MAX_AGE){
System.out.println("年龄正确");
}else{
System.out.println("年龄有误");
}
System.out.println(Math.PI); // java中的Math类型定义的常量PI.
}
}
final class Aoo{ // final修饰的类不能被继承。
}
/*
class Boo extends Aoo{ // 编译错误: Aoo是final修饰的类,不能出现子类。
}
*/
class Boo{
public final void print(){ // final修饰的方法,不能被重写。
System.out.println("我是Boo中的print方法。");
}
}
class Coo extends Boo{
int count ;
final int size = 100; // final修饰的变量,定义的时候就赋值,赋值之后,值不可以变。
//size = 120 ;// 编译错误: final修饰的成员变量的值不可以修改了。
// 定义常量: ① 用static和final 一起修饰 [static让这个常量可以通过类名调用。 final让这个变量的值不变,那就是常量了。]
// ② 建议常量的名字全大写。
static final double PI = 3.14;
// 练习: 最大年龄120 。 最小年龄0.
static final int MAX_AGE = 120 ;
static final int MIN_AGE = 0;
/*
public void print(){ // 编译错误: 父类中的print方法,是final修饰的,不能被重写。
}
*/
}
对象数组
- 所谓对象数组,对象数组的元素不是基本类型,而是引用类型
- 对象数组的初始化和基本类型数组的初始化方式一样,只不过元素是对象引用而已。
- 对象数组创建之后,元素的默认值是null.
package oopday3;
public class TestPoint {
public static void main(String[] args) {
// 1. 创建5个Point对象,保存在一个变量中。 -- 5个数据,要存在一个变量中, 只有数组结构符号这个要求。
Point[] ps = new Point[5]; // 创建的数组对象
for(int i = 1; i < 6; i ++){
Point p = new Point(); // 创建对象
p.x = i ;
p.y = i ;
ps[i-1] = p ; // 给数组元素赋值。
}
// 2.修改 ps中的元素的x,y
System.out.println(ps[1].x + ":" + ps[1].y);
ps[1].x = 10 ;
ps[1].y = 10 ;
System.out.println("修改之后:");
System.out.println(ps[1].x + ":" + ps[1].y);
// 3. 输出所有的点的数据
for(int i = 0 ; i < ps.length ; i++){
// System.out.println(ps[i]); // ps[i]输出的是Point的"包名.类名@16进制的整数"
System.out.println(ps[i].x +"," + ps[i].y);
}
}
}
二维数组
- 数组的元素可以为任何类型,也包括数组类型。
- 二维数组: 数组的元素如果是一个数组,那么这个数组就被称为二维数组。
- 代码
package oopday3;
import java.util.Arrays;
// 二维数组: 数组中的元素依然是一个数组,就被称为二维数组。
public class Array2Demo {
public static void main(String[] args) {
// 基本数据类型的2维数组 :
int[][] arr = new int[5][];// 定义二维数组的时候,需要设置第一维长度。
arr[0] = new int[5]; // 数组的第一个元素,是一个长度为5的整形数组。
arr[1] = new int[5];
arr[2] = new int[4];
arr[3] = new int[4];
arr[4] = new int[4];// 由于数组中的元素的长度不一致,所以呢,定义这个二维数组的时候,只能确定第一维的长度,第二维的长度由每个元素决定。
// 遍历二维数组: 循环的嵌套
// 把数组的每个元素输出在一行显示
for(int i = 0 ; i < arr.length ;i ++){
// arr[i] 就是数组中的每个元素(这个元素还是数组,素以呢就继续使用for循环,遍历这个元素)
for(int j = 0 ; j < arr[i].length ; j ++){
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
System.out.println("_________________________________");
// 定义一个长度是5的数组,数组的元素还是一个长度为5的数组。
int[][] arr1 = new int[5][5];
// 遍历数组
for(int i=0 ; i < arr1.length ; i++){
System.out.println(Arrays.toString(arr1[i]));
}
// 二维数组初始化
System.out.println("_________________________________");
String[][] strs = {{"A","A","A","A"},{"B","B","B","B"},{"C","C","C","C"}};
for(int i=0 ; i < strs.length ; i++){
System.out.println(Arrays.toString(strs[i]));
}
// 二维数组定义
Point[][] ps = {{new Point(1,2) , new Point(1,2)} ,
{new Point(1,2) , new Point(1,2)},
{new Point(1,2) , new Point(1,2)},
{new Point(1,2) , new Point(1,2)}};
System.out.println("------------------------");
for(int i=0 ; i < ps.length; i ++){
System.out.println(ps[i][0].x +"," +ps[i][0].y +"\t" + ps[i][1].x + ","+ ps[i][1].y );
}
}
}
抽象类:abstract
- 用abstract关键字修饰的类称之为抽象类。
- 抽象类不能实例化,抽象类的意义在于“被继承”。
- 抽象类为子类“抽象”出公共部分,通常也定义了子类所必须实现的抽象方法。
- 一个抽象类可以有抽象方法,也可以有非抽象方法
- 一个类继承了抽象类,那么必须实现父类的抽象方法,除非该类依然定义为抽象类。
- 抽象类的使用
package oopday3;
import java.security.AlgorithmConstraints;
// abstract : 抽象, java关键字abstract ,用于修饰类,表示这个类是一个抽象类。
// *** 抽象类中,可以定义抽象方法。
public abstract class Shape {
int x ;
int y ;
/*
public double area(){ // 实现了的方法。 这里并不能体现出面积的计算公式 ,因为当前这个类型不太方便计算面积。
return 0;
}
*/
public abstract double area();// 这个是一个抽象方法, 只是定义了方法,并没有实现方法。
// 定义了一个规范,那就是所有Shape类型的子类,都必须实现area方法。
}
class Circle extends Shape{ // 继承了抽象类之后, 必须要重写父类的抽象方法。
int r ; // 半径
public double area(){ // 方法的实现 。
return Math.PI * r * r ; // 计算面积的公式。
}
}
class Rect extends Shape{
double w;
double h;
public double area(){// 继承了抽象类之后, 必须要重写父类的抽象方法。
return w * h;
}
}
/* 编译错误: 非抽象类中,不能出现抽象方法。
class Other{
public abstract double area();
}
*/
/* 抽象类中,可以有抽象方法,也可以没有抽象方法。
abstract class Other{
public double area(){
return 10 ;
}
}
*/
// 如果父类有抽象方法,子类要么实现这些抽象方法,要么自己依然定义为抽象类。
abstract class SanJiao extends Shape{
}
接口的使用
- 接口可以看成是特殊的抽象类。接口中只能定义常量和抽象方法。
- 一个类可以实现多个接口,多个接口之间用逗号分隔。这个类需要实现所有接口的抽象方法。
- 接口可以作为一种类型声明变量,一个接口类的变量可以引用实现了该类的对象。通过该引用,调用接口中的方法(实现类中提供了接口方法的实现)
- 接口间可以存在继承关系,一个接口可以通过extends关键字继承另外一个接口。
- 子接口继承了父接口中定义的所有方法
- 代码
package oopday4;
// interface - 接口: 接口用于制定规范,java中的接口类型中,只能写常量的定义, 抽象方法的定义。
// 定义好接口之后, 可以通过类实现接口 ,然后类中重写这些接口中定义的方法。
// 接口是抽象的,不能创建对象。
public interface Animal {
// 定义常量
static final String OLDER = "女娲";
// 定义抽象方法。
void eat(); // 接口中的方法默认是public abstract . 所以可以省略这两个关键字。
public abstract void sleep();
public abstract void sing();
}
// Cat实现了Animal接口, 所以Cat是Animal.
class Cat implements Animal{// 类需要实现接口中的所有抽象方法, 否则把类设置为abstract类。
String pinZhong;
String name;
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void sleep() {
System.out.println("睡觉");
}
@Override
public void sing() {
System.out.println("喵喵叫....");
}
public void catchMouse(){
System.out.println("抓老鼠");
}
}
- 一个类继承多个接口
package oopday4;
// Intro ,定义一个打印成员变量的方法。
// Intro制定了一个规范, 实现Intro接口的类,必须提供一个print方法。
public interface Intro {
void print();
}
// 一个类可以实现多个接口。那么这个类需要实现每个接口中定义的方法
class Mouse implements Animal , Intro {
String name;
String color;
@Override
public void eat() {
System.out.println("老鼠在偷吃东西。");
}
@Override // 空实现: 语法规则是有{}的 , 但是{}内部没有代码。
public void sleep() {
}
@Override
public void sing() {
}
@Override
public void print() {
System.out.println(this.name + "是" + this.color);
}
}
- 子接口继承父接口
package oopday4;
// ** java中规定extends 是单继承(一个类只能extends一个父类)。
// java中可以一个类同时实现多个接口。 (一个类能够 implements 多个接口。)
// java中的类可以继承父类,同时实现某些接口。
//Tiger 继承了Animal中的eat , sleep , sing等方法。
// ** 因为Tiger 依然是一个interface类型, 所以这里不重写父接口中的方法。
public interface Tiger extends Animal {
public abstract void milk(); // Tiger具有的方法
}
class HuaNan implements Tiger{
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void sing() {
}
@Override
public void milk() {
}
}
class DongBeiTiger implements Tiger{
@Override
public void eat() {
}
@Override
public void sleep() {
}
@Override
public void sing() {
}
@Override
public void milk() {
}
}
// HeiLoingJiangTiger :可以是 DongBeiTiger , Tiger , Animal , Intro.
class HeiLoingJiangTiger extends DongBeiTiger implements Intro{
@Override
public void print() {
}
}
内部类
- 一个类可以定义在另外一个类的内部,定义在内部的类称为内部类,其所在的类称之为外部类。
- 定义在内部的类,一般只服务于其外部类,堆外部类具备可见性,内部类可以直接使用外部类的成员及方法。
- 通常内部类不能独立于外部类存在,一般设置内部类为private。在外部类之前创建内部类对象,如下语法规则:
- 当一个类存在的价值仅仅为某一个类服务时,应使其成为那个类的内部类。
- 内部类一般情况下堆外不可见,除了包含它的外部类外,其他类无法访问到它。
- 内部类可以很方便的访问其外部类的私有属性
- 代码
package oopday4;
// 内部类:类的内部包含一个其它类,这个被包含的类,就被称为内部类。
// *** 内部类对象的创建,需要依赖于外部类对象。 因此先创建外部类对象,然后借助于外部类对象,创建内部类。
// 语法规则:外部类的对象.new 内部类();
public class Outer {
private String name ;
private int x ;
private int y ;
public void out(){
System.out.println("我是外部类的out方法。");
}
// 内部类
class Inner{
String name ; // 内部类的成员变量。
String hobby; // 内部类的成员变量。
// 定义一个方法, 用于输出外部类的成员变量的值。
public void print(){
// 访问外部类的成员变量。
// 内部类的成员变量name , 和外部类的成员变量name重名。 优先使用内部类的成员变量。
// Outer.this.name : 通过外部类的类名.this来访问外部类的成员变量。
System.out.println(Outer.this.name + ":" + x +":" + y);
System.out.println("内部类的成员变量:" + name + hobby);
// 调用外部类的成员方法。
out();
}
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.out();// 调用自己类中定义的方法。
outer.x = 10;
outer.y = 20 ;
outer.name = "xxxx";
//Inner in = new Inner(); // 编译错误:Inner是Outer中定义的内部类, 语法规定需要"外部类的对象.new 内部类()"创建对象。
Inner in = outer.new Inner();
in.name="小红";
in.hobby="喝奶茶";
// 调用内部类的方法。
in.print();
// 链式操作
System.out.println("____________________________");
Inner in1 = new Outer().new Inner();//
in1.print();
}
}
匿名内部类
- 如果在一段程序中需要创建一个类的对象(通常这个类需要实现某个接口或者继承某个类),而且对象创建后,这个类的价值也就不存在了,这个类可以不必命名,称之为匿名内部类。
package oopday4;
import java.util.Timer;
import java.util.TimerTask;
// 测试匿名内部类的使用
public class TestInner {
public static void main(String[] args) {
Timer timer = new Timer();
//MyTask task = new MyTask();
TimerTask task = new MyTask();
// 每隔1秒,执行一次task中的run方法。
timer.schedule(task , 1000, 1000 );
}
}
// TimerTask是抽象类,无法创建对象, 所以需要完成TimerTask的实现类, 。
class MyTask extends TimerTask{
@Override
public void run() {
System.out.println("该睡觉了......");
}
}
局部内部类
- 定义在方法中的类称为局部内部类
package oopday4;
//局部内部类 : 定义在类中的某个方法内部的类被称为局部内部类。
public class TestInner2 {
public void fun(){
class Inner{ // 局部内部类
public void fun(){
System.out.println("这个是局部内部类的方法fun。");
}
}
Inner inner = new Inner();
inner.fun();// 调用的是内部类的inner方法。
}
public static void main(String[] args) {
TestInner2 ti2 = new TestInner2();
ti2.fun(); // fun()方法被调用的时候, 局部内部类对象被创建, 对象创建之后,会调用内部类的方法。
}
}
练习:员工的管理
员工类
package oopday4.emp;
import java.util.Date;
// 定义一个类型
public class Emp {
private String name;
private String sex;
private Date hireDate;
private Integer deptId;
private Double salary;
private String job;
public Emp(){ // 无参构造函数
}
public Emp(String name, String sex, Date hireDate,
Integer deptId, Double salary, String job) {
this.name = name;
this.sex = sex;
this.hireDate = hireDate;
this.deptId = deptId;
this.salary = salary;
this.job = job;
}
public void come(){
System.out.println("上班打卡");
}
public void goHome(){
System.out.println("下班打卡");
}
public void diary(){
System.out.println("写日报.");
}
public String getName(){
return this.name;
}
}
操作员工的接口
package oopday4.emp;
// dao: 数据库访问对象
public interface EmpDao {
// 定义方法。
public abstract boolean addEmp(Emp emp); // 增加
boolean delEmp(String name); // 删除
boolean updateEmp(Emp emp); // 修改
Emp[] selectEmp();// 查询员工
}
操作员工接口的实现类
package oopday4.emp;
import java.util.Date;
// EmpDaoImpl ,Impl是implements的简写.
public class EmpDaoImpl implements EmpDao{
@Override
public boolean addEmp(Emp emp) {
if(emp != null){
return true;
}
return false;
}
@Override
public boolean delEmp(String name) {
if(name != null){
return true;
}
return false;
}
@Override
public boolean updateEmp(Emp emp) {
if(emp != null){
return true;
}
return false;
}
@Override
public Emp[] selectEmp() {
Emp[] emp =new Emp[3];
for(int i =0 ; i < emp.length ; i++){
emp[0] = new Emp("tom"+i , "男生" , new Date() ,
i+1 , 3000.0+i*300 , "xx");
}
return emp;
}
}
测试类
package oopday4.emp;
import java.util.Scanner;
public class TestEmp {
public static void main(String[] args) {
// Scanner 类 , sc 是一个引用 , new Scanner(System.in)是一个对象。
Scanner sc =new Scanner(System.in);
// 定义变量
Emp emp ;
EmpDaoImpl dao = new EmpDaoImpl();
boolean b ;
while(true){
print();
int i = sc.nextInt();
switch (i){
case 0:
return ;
case 1:
emp = new Emp();
b = dao.addEmp(emp);
if(b){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
break;
case 2:
emp = new Emp();
b = dao.addEmp(emp);
if(b){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
break;
case 3:
String name = "tom";
b= dao.delEmp(name);
if(b){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
break;
case 4:
Emp[] emps = dao.selectEmp();
for(int j=0 ; j < emps.length ; j++){
System.out.println(emps[j].getName());
}
}
}
}
public static void print(){
System.out.println("**********员工管理********");
System.out.println("**********1.增加员工****");
System.out.println("**********2.修改员工****");
System.out.println("**********3.删除员工****");
System.out.println("**********4.查询员工****");
System.out.println("**********0.退出系统****");
System.out.println("*************************");
}
}
值传递&引用传递
- 如果一个方法的参数为引用类型,直接修改改参数辉对其造成影响
- 如果一个方法的参数为引用类型,该方法中又创建了新对象,不会对实际参数造成影响。
- 如果一个方法的参数为字符串,该方法中赋值了一个新字符串,不会对实际参数造成影响
- 如果一个方法的参数为原始类型,该方法不会对实际参数造成影响。
- 代码
package oopday5;
// ** arguments : 参数
public class ArgumentsDemo {
public static void main(String[] args) {
Point point = new Point();
point.x = 10 ;
point.y = 10 ;
point.print(); // 输出点 (10,10)
// testPoint(point); // 实际参数, 是引用类型。
// point.print(); // 输出点(11 ,10)
testPoint1(point); // 实际参数,是引用类型。
point.print(); // 输出点(10,10)
System.out.println("---------------特殊的字符串引用类型------------------");
String str = "你好世界"; // 创建对象, 赋值给引用。
testStr(str);
System.out.println("最后的str:" + str); //
System.out.println("----------值传递----------");
int i = 3;
int k = 5;
testInt(i,k);
System.out.println("最后的i,k:" + i +"," +k);
}
public static void testPoint(Point p){ // 参数是引用类型
p.right(); // 右边移动一个位置。
}
public static void testPoint1(Point p){
p = new Point(); // 给引用赋值了一个新对象。 实际参数传入的引用对应的对象不会被修改。
p.right();
}
public static void testStr(String str){
System.out.println("str:" + str); // 使用。
// 每个字符串都是一个新对象。
str = "中国"; // 把字符串对象"中国"的引用地址 ,赋值给str这个应用了。
System.out.println("str:" + str);
}
public static void testInt(int i , int k){ // 基本数据类型, 值传递, 把i,k的值取出来传入这个方法中使用。
i = 10*i;
k = 10*k;
System.out.println("i=" +i +"," + "k=" +k);
}
}
== 和equals的区别
- == , 基本数据类型,判断数据值是否相等
- == , 引用类型,判断引用类指向的地址是否相等,即是否为同一个对象。
- equals是父类Object中提供的的一个equals方法,用于制定两个对象是否相等的比较规则。
- Object中的equals默认是比较两个对象的地址是否相同。
- 子类重写父类的equals方法,自定义比较规则: 比如: 判断两个点是否相同,可以判断两个点的x位置和y位置同时相等,则两个点相同。
- 如果两个对象比较equals为true ,那么这依然不能说明是否为同一个对象。
- 重写类的equals方法: 使用idea的自动生成
package oopday5;
import java.util.Objects;
// 定义类型: 成员变量,成员方法。
// *** java中的所有类都有父类。 如果你定义的类型没有写extends那个父类,那么这个类的父类就是Object 。 一般省略extends Object.
// 继承父类的属性 和 方法 ,如果父类的方法不满足子类的需求, 就重写父类的方法。
public class Point extends Object {
int x;
int y;
public void up(){
this.y ++ ;
}
public void down(){
this.y --;
}
public void left(){
this.x --;
}
public void right(){
this.x ++;
}
public void print(){
System.out.println("(" + this.x + "," + this.y + ")");
}
@Override
public boolean equals(Object o) {
// 判断是否为自己
if (this == o)
return true;
// 和空比较,直接就是false.
// getClass() != o.getClass() .如果是不同的类型进行比较,直接就是false.
// ** getClass() ,获取到的是对象的类型。
if (o == null || this.getClass() != o.getClass())
return false;
// 因为o不是null, 又是同种类型,所以就强制数据类型。
Point point = (Point) o;
// x ,y 同时相等,返回true, 否则返回false.
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
- 参考代码1
package oopday5;
// == : 用于判断基本数据类型的数据值是否相等 , 用于判断引用类型的地址是否相等。
// ** 如果引用地址相等,说明一定是指向的同一个对象,否则,说明是指向不同的对象。
// equals: 类中的方法 , 用于判断对象的内容是否相等。
public class EqualsDemo {
public static void main(String[] args) {
int a = 10 ;
int b = 10 ;
System.out.println(a == b); // true.
Point p1 = new Point();
p1.x = 10 ;
p1.y = 10 ;
Point p2 = new Point();
p2.x = 10 ;
p2.y = 10 ;
System.out.println("p1:" + p1);
System.out.println("p2:" + p2);
System.out.println(p1 == p2); // 用于==判断引用类型地址是否相等。 false
Student stu1 = new Student("1001" , "小红" ,"java2202");
Student stu2 = new Student("1001" , "小红" ,"java2202");
System.out.println("stu1:" + stu1);
System.out.println("sut2:" +stu2);
System.out.println(stu1 == stu2);
Student stu3 = stu1;
System.out.println(stu3 == stu1); // true. 如果两个引用使用==判断,结果为true,说明这是引用的同一个对象。
// *** 如果规定 Point类型,如果x ,y 和另一个对象的x,y相等,我们就认为这两个点是同一个点。
// 如果规定Student类型,code, name, className各自相等,就认为是同一个学生。
// ** 如果没有重写equals方法
// System.out.println(p1.equals(p2)); // false . ** Object中定义的equals方法,就是用==判断两个对象的地址是否相等。
// ** 如果重写了equals方法
System.out.println(p1.equals(p2)); // true , 重写了equals方法,制定了比较规则: x ,y同时相等,则两个点相等。
Point p3 = new Point();
p3.x = 10 ; p3.y = 5;
System.out.println(p1.equals(p3)); // false 。 因为x和y没有同时相等。所以equals方法,返回false.
System.out.println(p1.equals(null)); // false. 空不能和任何对象相等。
System.out.println(p1.equals(stu3)); // false. 不同类型,不能比。
System.out.println(p1.equals(p1)); // true , 自己和自己相等。
}
}
- 参考代码2
package oopday5;
import java.util.Objects;
public class EqualsDemo1 {
public static void main(String[] args) {
String str = "abc"; // 创建了字符串对象,赋值给str
String str1 = new String("abc"); // 创建了字符串对象,赋值给str1.
System.out.println(str == str1); // false , 引用类型,== 判断对象的地址是否相等。
System.out.println(str.equals(str1)); // true , equals方法,用于判断对象的内容是否相等。
// 1. 创建学生对象
Student stu1 = new Student("1001" , "小红" ,"java2202");
Student stu2 = new Student("1001" , "小红" ,"java2202");
Student stu3 = stu1;
Student stu4= new Student("1002" , "小红" ,"java2202");
// 2. 判断对象是否相等
System.out.println(stu1.equals(stu3)); // true
System.out.println(stu1.equals(stu2)); // true
System.out.println(stu1.equals(null)); // false
System.out.println(stu1.equals(stu4)); // false
// 3. 使用Objects.equals(obj ,obj); 判断对象是否相等。
System.out.println(Objects.equals(stu1, stu4)); // false
Car c = new Car();
c.pinPai = "宝马";
Car c1 = new Car();
c1.pinPai ="宝马";
System.out.println("====================================");
System.out.println(Objects.equals(c ,c1)); // false . Objects.equals 进行c,c1的比较的时候,调用c,c1的类型Car中的equals方法。
// Car中的equals方法,是继承的Object中的,比较的是两个对象的地址是否相等。
ChangAnCar ca = new ChangAnCar();
ca.pinPai = "xx";
ca.name ="zzz";
ChangAnCar ca1 = new ChangAnCar();
ca1.pinPai = "xx";
ca1.name ="zzz";
System.out.println(ca.equals(ca1)); // true
System.out.println(Objects.equals(ca,ca1)); //Objects.equals(ca,ca1) ,本质就是调用ca.equals(ca1)
System.out.println(ca.equals(c)); // c instanceof ChangAnCar 是false.
System.out.println("c instanceof ChangAnCar :" + (c instanceof ChangAnCar ));
}
}
面向对象编程&面向过程编程
- Object Oriented Programming 简称OOP: c++ ,java , c#
- Procedure Oriented Programming 简称POP: c语言
- 面向过程,侧重于分析问题的步骤和具体细节,亲力亲为地去完成每一步都是基于上一步去完成
面向对象,侧重于创建解决问题的实体,通过实体互相配合去完成每个实体解决问题的细节实质也是面向过程的
面向对象三大特性
封装
- 通过private关键字对成员变量进行修饰,提供公开的getter和setter方法用于简单地对成员变量进行取值和赋值。
- 可以隐藏具体的细节,实现代码安全
- 在Idea中,自动生成getter/setter的快捷键为: 右键--generate或alt+insert -->getter and setter 选择要生成的成员变量
继承
- A类通过extends关键字,继承B类,Java是中是单继承,一个子类只能有一个父类,一个父类可以有多个子类
- 继承能够提高代码的重用性,Java是中是单继承,一个子类只能有一个父类,一个父类可以有多个子类
多态
- 对象是多种状态的。
- 父类的引用指向子类的对象 Father s=new Son();
package oopday5;
public class Printer {
public void print(){
System.out.println("打印机可以打印");
}
public static void main(String[] args) {
// 多态
Student stu1 = new Student();
stu1.name = "alice";
Student stu2 = new Student();
stu2.name = "tom";
stu1.name = "jack";
Printer p = new Printer3D() ;
Printer p1 = new PrinterBlack() ;
Printer p2 = new PrinterBlack() ;
p.print();
p1.print();
p2.print();
}
}
class Printer3D extends Printer{
@Override
public void print() {
System.out.println("3D打印机.....");
}
}
class PrinterBlack extends Printer{
@Override
public void print() {
System.out.println("黑白打印机.....");
}
}
class PrinterColor extends Printer{
@Override
public void print() {
System.out.println("彩色打印机");
}
}
静态代码块和非静态代码块
public class CodeBlockDemo {
private int x ;
static double pi;
static { //静态代码块, 类加载完成的时候,执行静态代码块。
System.out.println("这个是静态代码块");
pi = 3.14;
}
{ // 非静态代码块 , 创建对象的时候,执行非静态代码块。
System.out.println("非静态的代码块");
this.x = 10 ;
}
- 参考代码
package oopday5;
import java.util.Objects;
// 静态代码块
// 非静态代码块
public class CodeBlockDemo {
private int x ;
static double pi;
static { //静态代码块, 类加载完成的时候,执行静态代码块。
System.out.println("这个是静态代码块");
pi = 3.14;
}
{ // 非静态代码块 , 创建对象的时候,执行非静态代码块。
System.out.println("非静态的代码块");
this.x = 10 ;
}
public CodeBlockDemo(){
System.out.println("对象创建成功了。");
}
// 要求: 类加载的时候,给变量pi初始化为3.14 . 对象创建的时候,给变量x ,初始化为10.
public static void main(String[] args) {
CodeBlockDemo cbd = new CodeBlockDemo();
System.out.println(CodeBlockDemo.pi);
System.out.println(cbd.getX());
CodeBlockDemo cbd1 = new CodeBlockDemo();
System.out.println(cbd1.getX());
}
// 成员方法
public void add(){
x++;
}
public void add(int k){
this.x = x*k;
}
// get & set
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
// equals & hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CodeBlockDemo)) return false;
CodeBlockDemo that = (CodeBlockDemo) o;
return getX() == that.getX();
}
@Override
public int hashCode() {
return Objects.hash(getX());
}
}
class Aoo{
void t(){
}
}
class Boo extends Aoo{
public Boo(){
super();
// this();
}
public Boo(int k ){
System.out.println("xxx");
// super();
// this();
}
void t(){
}
}
面向对象阶段部分面试题
面向对象的特点:
封装、继承、多态
你是怎么理解面向对象的
面向对象是一种编程思想,万物皆对象,对象有属性、行为。每个对象是唯一的,且都可以拥有它的属性与行为。我们就可以通过调用这些对象的方法、属性去解决问题。
面向对象和面向过程区别?
面向过程
优点: 性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗 资源;比如单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发,性能是 最重要的因素。
缺点: 没有面向对象易维护、易复用、易扩展
面向对象
优点: 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特 性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点: 性能比面向过程低
重载和重写区别
重载(Overloading)
(1) 方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数类型。重载Overloading是一个类中多态性的一种表现。
(2) Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
(3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
重写(Overriding)
(1)父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的;
接口和抽象类的区别是什么 ?
(1)接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始 接口方法可以有默认实现),抽象类可以有非抽象的方法
- (2)接口中的实例变量默认是 final 类型的,而抽象类中则不一定
- (3)一个类可以实现多个接口,但最多只能实现一个抽象类
- (4)一个类实现接口的话要实现接口的所有方法,而抽象类不一定
- (5)接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口 的对象从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
-
成员变量与局部变量的区别有哪些?
-
(1)从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的 变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所 修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员 变量和局部变量都能被 final 所修饰;
(2)从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存 在于堆内存,局部变量存在于栈内存
(3)从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对 象的创建而存在,而局部变量随着方法的调用而自动消失。
(4)成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情 况例外被 final 修饰的成员变量也必须显示地赋值);而局部变量则不 会自动赋值。
-
创建一个对象用什么运算符?对象实体与对象引用有何不同?
-
new 运算符。
new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向 0 个或 1 个对象 (一根绳子可以不系气球,也可以系一个气球);一个对象可以有 n 个引用指向 它(可以用 n 条绳子系住一个气球)。
-
什么是方法的返回值?返回值在类的方法里的作用是什么?
-
方法的返回值:是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。
返回值的作用:接收出结果,使得它可以用于其 的操作! -
一个类的构造方法的作用是什么?若一个类没有声明构造方法,该程序能正确执行吗 ?为什么?
-
主要作用:是完成对类对象的初始化工作。
可以执行。因为一个类即使没有声明 构造方法也会有默认的不带参数的构造方法。 -
构造方法有哪些特性?
-
(1)名字与类名相同;
(2) 没有返回值,但不能用 void 声明构造函数;
(3) 生成类的对象时自动执行,无需调用。
-
对象的相等与指向他们的引用相等,两者有啥不同?
-
对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是 否相等。
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
帮助子类做初始化工作
构造块、静态块、构造方法中的方法执行顺序是怎样的?
静态块 → 构造块 → 构造方
普通类和抽象类有哪些区别?
(1)普通类不能包含抽象方法,抽象类可以包含抽象方法。
(2)抽象类不能直接实例化,普通类可以直接实例化。
抽象类能使用final修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息。
补充:关于 final 关键字的一些总结
final 关键字主要用在三个地方:变量、方法、类。
(1)对于一个 final 变量,如果是基本数据类型的变量,则其数值一旦在初始 化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不 能再让其指向另一个对象。
(2)当用 final 修饰一个类时,表明这个类不能被继承。final 类中的所有成员 方法都会被隐式地指定为 final 方法。
(3)使用 final 方法的原因有两个。第一个原因是把方法锁定,以防任何继承 类修改它的含义;第二个原因是效率。在早期的 Java 实现版本中,会将
final 方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用 带来的任何性能提升(现在的 Java 版本已经不需要使用 final 方法进行 这些优化了)。类中所有的 private 方法都隐式地指定为 final。