携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第十四天,点击查看活动详情
总概:面向对象
面向对象三条主线:
1.java类及类的成员,属性 方法 构造器 代码块 内部类
2.面向对象的三大特征:封装 继承 多态 (抽象性)
3.其他关键字:this super static final abstract
interface package import
相关面试题
画出下面代码在执行时的内存分配情况
类和对象
创建是指通过new创建
jvm内存结构与对象的内存解析
jvm内存结构
编译完源程序以后,生成一个或多个字节码文件
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行.意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析.
所以内存解析是在执行java XXX时才进行.
虚拟机栈,即我们平时提到的栈结构.我们将局部变量存储到栈结构中.
堆,我们将new出来的结构(如 数组 对象)加载到堆空间中.补充:对象的属性(非static的)加载到堆空间内.
方法区:类的加载信息 常量池 静态域.
注意:内存解析的说明
引用类型的变量,只能存储两种值:null或地址值(含变量的类型)
对象的内存解析
对象数组的内存解析
类的成员之一属性
(成员变量)VS局部变量
局部变量不可以使用权限修饰符
补充:变量的分类
类的成员之二方法
void表示没有返回值
方法应用
题目一
package com.atguigu.obj;
public class MethodTest {
public static void main(String[] args){
Print p1 = new Print();
p1.length=10;
p1.width=8;
p1.print();
PrintTwo p2 = new PrintTwo();
System.out.println(p2.print(10,8));
}
}
class Print{
int length;
int width;
public void print(){
for(int i=0;i<width;i++){
for(int j=0;j<length;j++){
System.out.print("* ");
}
System.out.println();
}
}
}
class PrintTwo{
public int print(int m,int n){
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
System.out.print("* ");
}
System.out.println();
}
return m*n;
}
}
题目二
针对于第四题
package com.atguigu.obj;
public class ArryTest {
public static void main(String[] args){
//声明student类型的数组
Student[] stu=new Student[20];
for(int i=0;i<stu.length;i++){
//给数组元素赋值
stu[i]=new Student();
//给student对象的属性赋值
stu[i].number=(i+1);
//[1,6]
stu[i].state=(int)(Math.random()*(6-1+1)+1);
//[0-100]
stu[i].score=(int)(Math.random()*(100-0+1)+0);
}
for(int i =0;i<stu.length;i++){ //打印一下所有学生的信息
System.out.println(stu[i].getInfo());
}
for(int i =0;i<stu.length;i++){ //打印3年级学生的信息
if(stu[i].state==3){
System.out.println("dddddd");
System.out.println(stu[i].getInfo());
}
}
//使用冒泡排序按学生成绩排序,并遍历所有学生信息
for(int i= 0;i<stu.length-1;i++){
for(int j=0;j<stu.length-1-i;j++){
//从小到大排序(冒泡)
if(stu[j].score>stu[j+1].score){
//交换的是数组中的对象 不能是成绩
Student temp=stu[j];
stu[j]=stu[j+1];
stu[j+1]=temp;
}
}
}
System.out.println("*********");
for(int i = 0;i<stu.length;i++){
System.out.println(stu[i].getInfo());
}
}
}
class Student{
int number;
int state;
int score;
public String getInfo(){
String info="学号"+number+",年级"+state+",成绩"+score;
return info;
}
}
对上述代码进行优化(暂未完成)
return关键字
方法的重载(重点)
同一个类中,只要方法名相同,参数个数和参数类型不同即可构成重载.
可变个数形参的方法
注意:上述标红的部分不能在一个类中共存
-
jdk 5.0新增的内容
-
具体使用
- 可变个数形参的格式: 数据类型...变量名
- 当调用可变个数形参的方法时,传入的参数个数可以是0个,1个,2个...
- 可变个数形参的方法与本类中的方法名相同,形参不同的方法之间构成重载
- 必须声明在末尾
方法的参数传递----值传递机制
温习基础:变量赋值
方法形参的值传递机制:
思考下面为什么没有交换成功
package com.atguigu.obj;
/**
* @ClassName: Test
* @Description:
* @author:露
* @date:2022年8月26日 上午10:21:37
*/
public class Test {
public static void main(String[] args) {
Test test = new Test();
int a=1;
int b=2;
// int temp = a;
// a=b;
// b=temp;
test.swap(a, b);
System.out.println("a:"+a+" b:"+b); //第一种写法:a:2 b:1 第二种写法 a:1 b:2 (没有交换成功)
}
public void swap(int a,int b){
int temp = a;
a=b;
b=temp;
}
}
之所以没有实现m和n的交换,是因为swap方法执行完就销毁了,里面的变量也相应出栈.所以打印出来m,n的值没有变,还是main 方法一开始定义的值
此时 才能够交换成功
package com.atguigu.obj;
/**
* @ClassName: Test2
* @Description:
* @author:露
* @date:2022年8月26日 上午11:17:32
*/
public class Test2 {
public static void main(String[] args) {
Data data=new Data();
data.a=1;
data.b=2;
System.out.println("a:"+data.a+" b:"+data.b); //a:1 b:2
//交换两个变量的值
// int temp = data.a;
// data.a=data.b;
// data.b=temp;
// System.out.println("a:"+data.a+" b:"+data.b); //a:2 b:1
//
Test2 t2 = new Test2();
t2.swap(data);
System.out.println("a:"+data.a+" b:"+data.b); //a:2 b:1(交换成功
}
public void swap(Data data){
int temp = data.a;
data.a=data.b;
data.b=temp;
}
}
class Data{
int a;
int b;
}
内存分析(为什么能交换成功)
同理来改一下这个
练习
第一题
第二题
package com.atguigu.exe4;
/**
* @ClassName: PassObject
* @Description:
* @author:露
* @date:2022年8月26日 下午12:13:07
*/
public class PassObject {
public static void main(String[] args) {
PassObject po=new PassObject();
Circle c = new Circle();
po.printAreas(c,5);
System.out.println("now c的radius"+c.radius); //now c的radius5.0
}
//Circle c是一个地址值
public void printAreas(Circle c,int time){
System.out.println("Radius\t\tArea");
for(int i=1;i<=time;i++){
c.radius=i;
System.out.println(c.radius+"\t\t"+c.findArea());
}
}
}
方法:递归方法
考虑 一次只能跨1个或2个台阶,要跨完n个台阶,有多少种组合方法(这个可以参考上述7.3)
经典面试题
类的成员之三:构造器
构造器的作用:创建对象
通常我们都会给类提供一个空参的构造器
构造器与类同名
构造器的权限最大不能超过类的权限
多个构造器构成构造器的重载
属性赋值的先后顺序
前三个都可以叫做初始化 都只执行一次
this关键字
匿名对象
类的成员之四---代码块
package com.atguigu.java3;
/*
* 类的成员之四:代码块(或初始化块)
*
* 1. 代码块的作用:用来初始化类、对象
* 2. 代码块如果有修饰的话,只能使用static.
* 3. 分类:静态代码块 vs 非静态代码块
*
* 4. 静态代码块
* >内部可以有输出语句
* >随着类的加载而执行,而且只执行一次
* >作用:初始化类的信息
* >如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
* >静态代码块的执行要优先于非静态代码块的执行
* >静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
*
* 5. 非静态代码块
* >内部可以有输出语句
* >随着对象的创建而执行
* >每创建一个对象,就执行一次非静态代码块
* >作用:可以在创建对象时,对对象的属性等进行初始化
* >如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
* >非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
*
*/
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
System.out.println(desc);
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.age);
Person.info();
}
}
class Person{
//属性
String name;
int age;
static String desc = "我是一个人";
//构造器
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
//非static的代码块
{
System.out.println("hello, block - 2");
}
{
System.out.println("hello, block - 1");
//可以调用非静态结构
age = 1;
eat();
//也调用静态结构
desc = "我是一个爱学习的人1";
info();
}
//static的代码块
static{
System.out.println("hello,static block-2");
}
static{
System.out.println("hello,static block-1");
//只能调用静态结构
desc = "我是一个爱学习的人";
info();
//不可以调用非静态结构
// eat();
// name = "Tom";
}
//方法
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void info(){
System.out.println("我是一个快乐的人!");
}
}
面向对象的三大特点
封装性
为什么要引入封装性
封装性的体现:
只要涉及到四种权限的都体现封装性
四种访问权限修饰符
:类 类的属性\构造器\方法等都可以使用权限修饰符
不同包的子类中子类是指有继承关系的.
例如
继承性
java.lang.Object类的理解
Object是所有类的父类,也称根父类
方法的重写(重点)
注意:只有非static才有方法的重写,静态的方法不能被覆盖,是随着类的加载而加载的.
super关键字
super调用构造器
- 可以在子类的构造器中显式的使用super(形参列表),调用父类中声明的指定构造器
- super(形参列表)的使用,必须在子类构造器的首行
- 在类的构造器中,this(形参列表)或super(形参列表)只能二选一,不能同时出现
- 在构造器的首行,没有显示的声明this(形参列表)或super(形参列表),默认调用的是父类中的空参构造器.
- 在类的多个构造器中,至少有一个类的构造器中使用了super(形参列表),调用父类中的构造器
注意
子类对象的实例化过程
多态性
注意:为什么说方法调用在编译期间是无法确定的
总结:多态性是运行时的行为
相关面试题
第二个红框是多态性
注意
向下转型
Person p = new Man( ); //正确
Man m = p //错误 把父类对象转换为子类对象 就是向下转型 要使用强制 类型转换符 ,如下:
Man m = (Man)p
instanceof操作符
前提回顾:Person p2 = new Man( )
技巧1:new的谁,肯定是谁的类型(实例)
比如:Person p = new Man( )
p instance of Man 的结果是true
重要结论
关键看:1是不是2的实例(往往1是什么还要看它创建的时候new的谁) 如果1 instanceof 2 结果是true,则可以进行强转(编译和运行都通过)
强转----内存解析
多态性的使用体现
体现一
(下面都在一个文件里写的)
体现二
体现三
多态性的练习
package com.atguigu.duotai;
public class IntanceTest {
public static void main(String[] args) {
IntanceTest ts = new IntanceTest();
ts.method(new Student());
}
public void method(Person e){
String info=e.getInfo();
System.out.println(info);
if(e instanceof Graduate){
System.out.println("研究生 学生 人");
}else if(e instanceof Student){
System.out.println("学生 人");
}else{
System.out.println("人");
}
}
}
class Person {
protected String name="person";
protected int age = 50;
public String getInfo(){
return "Name:"+name+"\n"+"age:"+age;
}
}
class Student extends Person{
protected String school = "pku";
public String getInfo(){
return "Name:"+name+"\n age"+age+"\n school:"+school;
}
}
class Graduate extends Student {
public String major ="IT";
public String getInfo(){
return "Name:"+name+"\nage"+age+"\nschool:"+school+"\nmajor"+major;
}
}
//多态性
package com.atguigu.duotai;
public class Instance {
public static void main(String[] args) {
Instance is = new Instance();
Boolean isTrue=is.equalsArea(new Circle("blue",34.0,23),new MyRectangle("red",34.5,23.3,33.3));
System.out.println(isTrue); //false
is.displayGeometricObject(new Circle("blue",34.0,23)); //1661.9025137490005
}
public Boolean equalsArea(GeometricObject o1,GeometricObject o2){
return (o1.findArea()==o2.findArea())?true:false;
//return o1.findArea()==o2.findArea();
}
public void displayGeometricObject(GeometricObject o){
System.out.println(o.findArea());
}
}
class GeometricObject{
protected String color;
protected double weight;
public GeometricObject(String color,double weight){
this.color = color;
this.weight=weight;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double findArea(){
return 0.0;
}
}
class Circle extends GeometricObject {
private double radius;
public Circle(String color,double weight,double radius){
super(color,weight);
this.radius=radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea(){
return Math.PI*radius*radius;
}
}
class MyRectangle extends GeometricObject{
private double width;
private double height;
public MyRectangle(String color, double weight, double width, double height) {
super(color, weight);
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public double findArea(){
return width*height;
}
}
重点笔试题
package com.atguigu.exer;
//考查多态的笔试题目:
public class InterviewTest1 {
public static void main(String[] args) {
Base1 base = new Sub1();
base.add(1, 2, 3); //sub_1
Sub1 s = (Sub1)base; //强转之后可以调用子类独有方法
s.add(1,2,3); //sub_2 //根据参数序列对应
}
}
class Base1 {
public void add(int a, int... arr) {
System.out.println("base1");
}
}
class Sub1 extends Base1 {
public void add(int a, int[] arr) { //重写了父类同名方法
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}