递归、封装、构造器和this关键字8

知识点1:递归方法

递归方法的使用
*
* 1. 递归方法:一个方法体内调用它自身。
* 2. 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
* 3. 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*    死循环是我们开发中要避免出现的。进而递归方法也必须满足执行的有限性。否则的话,会报StackOverflowError
  • 代码演示
public class RecursionTest {
    public static void main(String[] args) {
        RecursionTest test = new RecursionTest();
        int sum = test.sum1(100);
        System.out.println("总和为:" + sum);

        int result = test.f(10);
        System.out.println("结果为:" + result);
    }

    //计算1-num所有自然数的和
    public int sum(int num){
        int sum = 0;
        for (int i = 1; i <= num; i++) {
            sum += i;
        }
        return sum;
    }

    //递归实现:1-100所有自然数的和
    public int sum1(int num){
        if(num == 1){
            return 1;
        }else{
            return sum1(num - 1) + num;
        }
    }

    //递归实现1:计算n!
    public int multiply(int num){
        if(num == 1){
            return 1;
        }else{
            return multiply(num - 1) * num;
        }
    }

    //递归实现2:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
    // 其中n是大于0的整数,求f(10)的值。
    public int f(int num){
        if(num == 0){
            return 1;
        }else if(num == 1){
            return 4;
        }else{
            //错误的
//            return f(num + 2) - 2 * f(num + 1);
            //正确的
            return 2 * f(num - 1) + f(num - 2);
        }
    }

    //递归实现3:已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),
    //其中n是大于0的整数,求f(10)的值。
    public int func(int num){
        if(num == 20){
            return 1;
        }else if(num == 21){
            return 4;
        }else{
            return func(num + 2) - 2 * func(num + 1);
        }
    }

    //递归实现4:斐波那契数列: 1 1 2 3 5 8 13 21 34 55
    //f(1) = 1;f(2) = 1;f(n + 2) = f(n + 1) + f(n)

    //递归实现5:汉诺塔

    //递归实现6:IO流时File类的使用:遍历指定文件目录下的所有文件名
    public void printFileName(File file){
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(int i = 0;i < files.length;i++){
                printFileName(files[i]);
            }

        }else{
            System.out.println(file.getAbsolutePath());
        }
    }

    //递归实现7:快排

}

知识点2:面向对象的特征一:封装与隐藏

1. 封装性的体现之一:

1. 问题的引入
* 我们在创建一个类的对象之后,可以通过“对象.属性”的方式给属性赋值。默认情况下,赋的值需要满足指定的
* 数据类型和变量的存储值的范围。但是,在实际情况中,此属性的赋值,需要满足实际情况的一些限制条件。(比如:
* legs必须是正数、且是偶数)。又由于我们不能直接在属性的声明位置加上此限制语句,则必须通过方法的
* 方法的方式给属性赋值,在方法内设置限制条件。则我们设计一个SetXxx()的方法。将此方法暴露出去(设置为public)
* 同时,不允许在类的外部再直接修改此属性。则将此属性隐藏起来(设置为private)即可。
*
* 1.1 为了出了类之后,能调用此属性的值,我们还需要提供一个getXxx()的方法。此方法声明为public的即可。
*
* 2. 小结:设计类时,可以考虑把类中的属性私有化(private),同时提供公共(public)的get和set方法,进行获取和设置此属性的操作。
  • 代码演示
public class AnimalTest {
    public static void main(String[] args) {
        Animal ani = new Animal();

        ani.name = "佩奇";
//        ani.age = 1;
//        ani.legs = -4;
        ani.setLegs(14);
//        ani.legs = -14;//非法访问

        ani.info();

//        System.out.println(ani.legs);
        System.out.println(ani.getLegs());
    }
}

class Animal {
    String name;
    private int age;
    private int legs;//腿的个数

    //获取属性值的方法
    public int getLegs(){
        return legs;
    }
    //设置属性值的方法
    //设计一个方法,通过方法给legs属性赋值
    public void setLegs(int l){
        if(l >= 0 && l % 2 == 0 && l <= 30){
            legs = l;
        }else{
            System.out.println("传入的数据非法!");
        }
    }
    //提供age属性的get和set方法
    public int getAge(){
        return age;
    }

    public void setAge(int a){
        age = a;
    }


    public void info() {
        System.out.println("name = " + name + ", age = " + age + ",legs = " + legs);
    }
}

2. 封装性的体现之二:

在类中声明方法时,可以将此方法声明为private。表明此方法只能在类内部使用。
  • 代码演示
public void sort(Student[] stus,String sortMethod){
        if("ascend".equals(sortMethod)){

            for(int i = 0;i < stus.length - 1;i++){
                for(int j = 0;j < stus.length - 1 - i;j++){
                    if(stus[j].score > stus[j + 1].score){
                        swap(stus,j,j + 1);
                    }
                }
            }
        }else if("descend".equals(sortMethod)){
            for(int i = 0;i < stus.length - 1;i++){
                for(int j = 0;j < stus.length - 1 - i;j++){
                    if(stus[j].score < stus[j + 1].score){
                        swap(stus,j,j + 1);
                    }
                }
            }
        }else{
            System.out.println("排序方式有误!");
        }
    }

    private void swap(Student[] stus,int i,int j){
        Student temp = stus[i];
        stus[i] = stus[j];
        stus[j] = temp;
    }

3. 4种权限修饰符

3. 封装与隐藏,体现了设计java类及java类的内部结构时,其可被访问的权限的大小。
* java规定的4种访问权限修饰符:(从小到大的顺序)
*   private < 缺省 < protected < public
4. 我们可以使用4种权限修饰符修饰类及类的内部结构
*      > 可以使用4种权限修饰符修饰:属性、方法、构造器、内部类
*      > 可以使用缺省或public修饰符修饰:类。  (不能使用private\protected修饰类)

A

知识点3:类的成员之三:构造器

类的成员之三:构造器的使用
*
* 1. Constructor = 构造器  = 构造方法 = 构造函数
*    construct : 构建、构造
*
* 2.构造器的作用:
*   ① 创建类的对象 (或 类的实例化)
*   ② 可以初始化对象的信息(比如:属性的初始化)
*
* 3. 说明:
*  ① 任何一个类,如果没有显式提供构造器的话,系统都会默认提供一个空参的构造器
*  ② 自定义构造器的格式: 权限修饰符 类名(形参列表){  }
*  ③ 类中可以声明多个构造器,彼此构成重载
*  ④ 一旦显式的定义了类的构造器,则系统不再提供默认的空参的构造器。
  • 代码演示
public class PersonTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "吴";
        p1.age = 25;

        p1.eat();
        p1.sleep(6);

        System.out.println("####################");
        Person p2 = new Person("石");
        System.out.println(p2.name);//石
    }
}

class Person{

    //属性
    String name;
    int age;

    //构造器
    public Person(){
        System.out.println("hello,Person()");
    }

    public Person(String n){
        name = n;
    }

    public Person(int a){
        if(a >= 0 && a <= 130){
            age = a;
        }
    }

    //方法
    public void eat(){
        System.out.println("人每天需要吃饭");
    }
    public void sleep(int hour){
        System.out.println("人每天需要保证" + hour + "小时的睡眠");
    }


}

知识点4:类中属性赋值的位置及先后顺序

/**
 * 小结:类中属性赋值的位置及执行先后顺序测试
 *
 * 1. 类中的属性都可以在哪些位置赋值?
 *  ① 默认初始化
 *  ② 显式初始化
 *  ③ 构造器中初始化
 *
 *  ④ 在创建了对象之后,使用"对象.方法" 或 "对象.属性"的方式赋值
 *
 *
 * 2. 赋值的先后顺序:
 * ① - ② - ③ - ④
 *
 * 3. 上述的 ①、②、③过程,在对象实例化过程中,只能执行一次!
 *
 * @author tzm
 * @create 2020-07-03 14:40
 */
public class UserTest {
    public static void main(String[] args) {
        User u1 = new User();

        System.out.println(u1.name);

        User u2 = new User(10);
        System.out.println(u2.age);
    }
}

class User{
    String name = "Tom";
    int age = 1;

    public User(){}

    public User(int a){
        age = a;
    }
}

知识点5:this关键字的使用

1. this可以调用属性、方法、构造器

1. this调用属性、方法

2. this调用属性、方法:
*   this可以理解为:当前对象 或 当前正在创建的对象
*
*   2.1 在类的方法内或构造器内执行操作时,如果方法的形参或构造器的形参与类的属性名相同,则
*   必须显式的通过"this."的方式,表明调用的当前类的属性。如果省略了"this.",则认为调用的是形参。
*   2.2 在类的方法内或构造器内执行操作时,如果方法的形参或构造器的形参与类的属性名不相同时,
*   则调用类的属性时,前面修饰的"this."可以省略。
*
*   2.3 在类的方法中,我们可以通过"this."的方式调用本类中的其他方法。只是大多数情况下,我们都
*   省略了"this."。
  • 代码演示
class Person{
    private String name;
    private int age;

    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }

    public void eat(){
        System.out.println("人:吃饭");
        this.sleep();
    }
    public void sleep(){
        System.out.println("人:睡觉");
    }
}

2. this调用构造器

3. this调用构造器:
*   ① this调用构造器的格式:this(形参列表)
*   ② 在类的构造器中,我们可以显示的使用"this(形参列表)"的方式,调用本类中的其他构造器。
*   ③ “this(形参列表)”必须声明在类的构造器的首行!
*   ④ 在类的一个构造器中,只能最多声明一个“this(形参列表)”
*   ⑤ 如果一个类中声明了n个构造器,则最多有n - 1 个构造器中使用了“this(形参列表)”
  • 代码演示
class Person{
    private String name;
    private int age;

    public Person(){
//        this("吴");
        System.out.println("我是一个人。我刚出生,需要洗澡!");
    }
    public Person(String name){
        this();
        this.name = name;
    }

    public Person(String name,int age){
        this(name);
        this.age = age;
//        this.eat();
    }

}

知识点6:其他

1. javaBean

/**
 * 满足如下条件的类,可以称为是一个javabean:
 * >类是公共的
 * >有一个无参的公共的构造器
 * >有属性,且有对应的get、set方法
 *
 * @author tzm
 * @create 2020-07-03 15:12
 */
public class Student {

    private int number;//学号
    private int state;//年级
    private int score;//成绩

    public int getNumber() {
        return number;
    }

    public void setNumber(int n) {
        number = n;
    }

    public int getState() {
        return state;
    }

    public void setState(int s) {
        state = s;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int s) {
        score = s;
    }
}

2.UML类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w0Dg6GOQ-1593771349253)(assets/1593766536723.png)]

昨天作业重点题目

  • 数据类:Student类
//数据
class Student{
    int number;//学号
    int state;//年级
    int score;//成绩

    public void info(){
        System.out.println("number : " + number + ", state : " + state + ", score : " + score);
    }

}
  • 操作数据的工具类:StudentUtil
//操作数据的工具类
class StudentUtil{

    /**
     * 根据指定的条件,创建相应的学生数组,并给数组元素赋值
     * @param length 指定学生数组的长度
     * @param lowState  指定学生对象的年级的下边界
     * @param highState 指定学生对象的年级的上边界
     * @param lowScore  指定学生对象的成绩的下边界
     * @param highScore 指定学生对象的成绩的上边界
     * @return 返回满足指定条件的数组
     */
    public Student[] getStudentArray(int length,int lowState,int highState,int lowScore,int highScore){
        Student[] stus = new Student[length];//虚位以待
        for (int i = 0; i < stus.length; i++) {
            stus[i] = new Student();
            //给每一个学生的属性赋值
            //学号
            stus[i].number = i + 1;
            //年级:1-6
            stus[i].state = (int)(Math.random() * (highState - lowState + 1) + lowState);
            //成绩:0-100
            stus[i].score = (int)(Math.random() * (highScore - lowScore + 1) + lowScore);
        }

        return stus;
    }

    /**
     * 遍历学生数组,打印出指定年级学生的信息
     * @param stus 待遍历的数组
     * @param state 指定的学生年级
     */
    public void printStudentState(Student[] stus,int state){
        for (int i = 0; i < stus.length; i++) {
            if (stus[i].state == state) {
                stus[i].info();
            }
        }
    }


    /**
     * 排序Student数组:使用冒泡排序
     * @param stus
     * @param sortMethod
     */
    public void sort(Student[] stus,String sortMethod){
        if("ascend".equals(sortMethod)){

            for(int i = 0;i < stus.length - 1;i++){
                for(int j = 0;j < stus.length - 1 - i;j++){
                    if(stus[j].score > stus[j + 1].score){
                        swap(stus,j,j + 1);
                    }
                }
            }
        }else if("descend".equals(sortMethod)){
            for(int i = 0;i < stus.length - 1;i++){
                for(int j = 0;j < stus.length - 1 - i;j++){
                    if(stus[j].score < stus[j + 1].score){
                        swap(stus,j,j + 1);
                    }
                }
            }
        }else{
            System.out.println("排序方式有误!");
        }
    }

    private void swap(Student[] stus,int i,int j){
        Student temp = stus[i];
        stus[i] = stus[j];
        stus[j] = temp;
    }

    /**
     * 遍历Student数组
     * @param stus
     */
    public void print(Student[] stus){
        for (int i = 0; i < stus.length; i++) {
            stus[i].info();
        }
    }
}
  • 测试类
//测试类
public class Exer3 {
    public static void main(String[] args) {
        StudentUtil util = new StudentUtil();
//      创建20个学生对象
        Student[] stus = util.getStudentArray(20, 1, 6, 0, 100);

        //问题一:打印出3年级(state值为3)的学生信息。
        util.printStudentState(stus,3);
        //问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
//        util.sort(stus,"descend");

        //遍历操作
//        util.print(stus);
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在使用递归算法解决由 n 个关键字组成的序列的排序问题时,其时间复杂度通常为O(nlogn)。这是因为递归算法在处理排序问题时,每次都会将数据分成两半,并对每一半分别进行排序,这样的复杂度就是O(nlogn)。 ### 回答2: 使用递归算法求解由n个关键字组成序列的排序问题时,其时间复杂度为O(nlogn)。 递归排序算法的基本思想是将问题划分为若干子问题,分别解决后再将结果合并,其中最典型的递归排序算法是快速排序和归并排序。 快速排序是通过选择一个基准数,将序列分为左右两部分,然后递归地对左右两部分进行排序,最后再将左右两部分合并起来。其时间复杂度为平均情况下为O(nlogn),最坏情况下为O(n^2)。 归并排序将序列递归地分成两半,对每一半进行排序,最后再将已排序的两个子序列合并起来。其时间复杂度始终为O(nlogn)。 无论是快速排序还是归并排序,其递归的过程都需要对序列进行拆分和合并,而每次拆分都会产生O(logn)层的递归栈,每一层的操作复杂度是O(n),因此总体的时间复杂度为O(nlogn)。 需要注意的是,在某些特殊情况下,比如快速排序的基准数选择不当,可能会导致时间复杂度变为O(n^2),但是平均情况下,递归排序算法的时间复杂度都是O(nlogn)。 ### 回答3: 使用递归算法求解由n个关键字组成序列的排序问题时,其时间复杂度为O(nlogn)。 递归排序算法中最常见的是归并排序和快速排序。在归并排序算法中,序列被分割成两个子序列进行递归排序,然后再将已排序的子序列合并起来。而在快速排序算法中,通过选择一个基准值将序列划分成两个子序列,并递归地对两个子序列进行排序。 在归并排序算法中,每次递归调用都将序列一分为二,并对两个子序列进行排序,然后再合并起来。因此,递归树的高度为logn,每层的合并操作的时间复杂度为O(n),所以总的时间复杂度为O(nlogn)。 在快速排序算法中,每次递归调用都将序列划分成两个子序列,并递归地对两个子序列进行排序,然后再将两个已排序的子序列合并起来。虽然划分子序列的过程可能是线性复杂度的,但是平均情况下,划分子序列的操作可以近似看作是O(n)的。因此,每次递归调用的时间复杂度为O(n),递归树的高度为logn,所以总的时间复杂度为O(nlogn)。 所以,使用递归算法求解由n个关键字组成序列的排序问题时,其时间复杂度为O(nlogn)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值