变量类型、构造器、封装以及 LeetCode 每日一题

1.成员变量和局部变量

1.1成员变量和局部变量定义

成员变量指的是类里面定义的变量(field),局部变量指的是在方法里定义的变量。

成员变量无须显示初始化,系统会自动在准备阶段或创建该类的实例时进行默认初始化。

与成员变量不同,局部变量除了形参之外,都必须显示初始化。

命名规则:

  • 一个类里不能定义两个同名的成员变量,即使一个是类变量,一个是实例变量;
  • 一个方法里不能定义两个同名的方法局部变量,方法局部变量与形参也不能同名;
  • 同一个方法中不同代码块的代码块局部变量可以同名;
  • 如果先定义的代码块局部变量,后定义方法局部变量,前面定义的代码块局部变量与后面的方法局部变量可以同名。
  • Java 允许局部变量和成员变量同名,如果方法里的局部变量和成员变量同名,局部变量会覆盖成员变量,如果需要在这个方法里引用被覆盖的成员变量,可使用 this. (对于实例变量)或类名(对于类变量)作为调用者来限定访问成员变量。

1.2成员变量初始化和内存

当系统加载类或创建该类的实例时,系统自动为成员变量分配内存空间,并在分配空间后,自动为成员变量赋初值。

当程序第一次使用某个类时,系统会为类变量分配内存空间并初始化,这个类变量是属于这个类的,并不属于实例。虽然我们还是能通过实例去访问这个类变量,但是通过任何实例访问的类变量都是同一个值,因为实际我们还是通过实例所属的类去访问的,为了避免语义混淆,建议访问类变量的时候通过类去访问,而不是实例。

1.3局部变量

系统不会为局部变量执行初始化,这意味着定义局部变量后,直到程序为这个变量赋初值,系统才会为局部变量分配内存,并将处置保存到内存中。

局部变量不属于任何类或实例,它总是保存在其所在方法的栈内存中。

2 构造器

2.1 初始化

  • 当程序员调用构造器的时候,系统会先为对象分配内存空间,并完成默认初始化,这个对象已经产生了。接着构造器的执行体完成之后,这个对象作为构造器的返回值被返回。
  • 因为构造器一般需要被其它方法调用,因而常常把构造器设置成 public 权限。极端情况下,如设置为 protected,主要用于被其子类调用;把其设置为 private,阻止其他类创建该类的实例。

2.2 构造器重载

重载的规则和方法重载差不多。特殊的一点是,如果一个构造器的执行体完全包含另一个构造器的执行体,则可在方法 B 中调用 方法 A。为避免调用时重复创建对象,需要使用 this 关键字,如下面代码:

 1 public class Apple{
 2     public String name;
 3     public String color;
 4     public double weight;
 5     public Apple(){} //无参数构造器
 6     //两个参数构造器
 7     public Apple(String name , String color){
 8         this.name = name;
 9         this .color = color;
10     }
11     //三个参数构造器
12     public Apple(String name , String color , double weight){
13         //通过另一个重载的构造器初始化代码,调用两个参数的构造器
14         this(name , color);
15         this.weight = weight;
16     }
17 }

PS:使用this调用另一个构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句。

3 隐藏和封装

3.1理解封装

封装就是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

良好的封装可以实现一下目的:

  • 隐藏类的实现细节;
  • 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入逻辑控制,限制对成员变量的不合理访问;
  • 可进行数据检查,从而有利于保证对象信息的完整性;
  • 便于修改,提高代码的可维护性;

为了实现良好的封装,需要从两个方面考虑:

  • 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问和操作;
  • 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。

3.2使用访问控制符

Java 的 4 个访问控制级别顺序由小到大:

private -> default(不加控制符) -> protected -> public

详细说明:

  • private(当前类访问权限):如果类里某一成员(变量、方法、构造器)使用 private 修饰,则这个成员只能在当前类的内部访问。显然这个控制符用于修饰成员变量最合适,可以把成员变量隐藏在类的内部。
  • default(包访问权限):default 访问控制的成员或外部类可以被相同包下的其他类访问
  • protected(子类访问权限):被 protected 控制符修饰的成员,既可以被同一个包中的其它类访问,也可以被不同包中的子类访问。通常情况下,如果用 protected 修饰一个方法,通常是希望其子类来重写这个方法。
  • public (公共访问权限):最宽松的访问级别,被 public 修饰的成员,可以被所有类访问,不管访问的类是否在一个包内,是否具有父子继承关系。

对于外部类而言,只能用 public 和默认,因为外部类没有处于任何类内部,也就没有其所在类的内部、所在类的子类这两个范围。public 修饰的外部类可以被所有类使用,默认控制权限的外部类只能被同一个包中的其他类使用。

PS:如果一个Java源文件里定义的所有类都没有 public 修饰,则这个 Java 源文件的文件名可以是一切合法的文件名;若有 public 类,必须和 public 类名一致。

一个良好封装的Person类:

 1 public class person{
 2         //使用private修饰成员变量,将其隐藏起来
 3         private String name;
 4         private int age;
 5         //提供方法来操作name成员变量
 6         public void setName(String name){
 7             //执行合理性校验,用户名必须在2~6位之间
 8             if (name.length() > 6 || name.length() < 2){
 9                 System.out.println("您设置的人们不符合要求");
10                 return;
11             }else{
12                 this.name = name;
13             }
14         }
15         public String getName(){
16             return this.name;
17         }
18         //提供方法来操作age成员变量
19         public void setAge(int age){
20             //执行合理性,要求用户年龄在0-100之间
21             if (age > 100 || age < 0){
22                 System.out.println("您设置的年龄不合法");
23                 return;
24             }else{
25                 this.age = age;
26             }
27         }
28         public int getAge(){
29             return this.age;
30         }
31 }

PS:如果一个 Java 类的每个实例变量都被使用 private 修饰,并为每个实例变量都提供了 setter 和getter方法,那么这就是一个符合 JavaBean 规范的类。

控制符使用的一些基本原则:

  • 类里的绝大部夫成员变量应该用 private 修饰,只有一些 static修饰的、类似全局变量的成员变量,才考虑使用 public 修饰。除此之外,有些方法只用于辅助实现该类的其他方法,这些方法称为工具方法,也应该用private修饰。
  • 如果某个类要做其它类的父类,该类里包含的大部分方法可能仅希望被其子类重写,而不想被调用,则应该使用 protected 修饰这些方法。
  • 希望暴露出来给其他类自由调用的方法应该使用 public 修饰。因此类的构造器通过用 public 修饰从而允许在其他地方创建该类的实例。因为外部类通常都希望被其他类自由使用,所以大部分外部类用 public 修饰。

删除排序数组中的重复项(26题)

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2], 
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

我第一遍提交的时候发现速度排名有点慢,看了下评论才发现题目说这个数组是有序的,所以我的第一个答案是针对任何数组都可以删重。

 1 class Solution {
 2     public int removeDuplicates(int[] nums) {
 3         HashMap<Integer , Integer> map = new HashMap<Integer , Integer>();
 4         for (int i = 0; i < nums.length; i++){
 5             if(map.get(nums[i]) == null)
 6                 map.put(nums[i] , 0);
 7         }
 8         int count = 0;
 9         for(int i = 0; i < nums.length; i++){
10             if(map.get(nums[i]) == 0){
11                 nums[count] = nums[i];
12                 count ++;
13                 map.put(nums[i] , 1);
14             }
15         }
16         for(int i = count;i < nums.length; i++){
17             nums[i] = 0;
18         }
19         return count;
20     }
21 }

第二个答案是针对题目所说的有序数组,速度超过99.2%的人。

 1 class Solution {
 2     public int removeDuplicates(int[] nums) {
 3         int count = 1;
 4         for(int i = 1; i < nums.length; i++){
 5             if(nums[i] > nums[count-1]){
 6                 nums[count] = nums[i];
 7                 count ++;
 8             }
 9         }
10         for(int i = count;i < nums.length; i++){
11             nums[i] = 0;
12         }
13         return count;
14     }
15 }

原文地址:https://www.cnblogs.com/carlosouyang/p/10742299.html

时间: 2024-08-30 17:31:57

变量类型、构造器、封装以及 LeetCode 每日一题的相关文章

类和对象,以及 LeetCode 每日一题

所有类都是引用类型. 1 定义类 类是某一批对象的抽象. 1.1 定义类的语法: 1 [修饰符] class 类名{ 2 零到多个构造器定义 3 零到多个成员变量 4 零到多个方法 5 } 对于一个类定义而言,可以包含三种最常见的成员:构造器.成员变量.方法 修饰符可以是 public.final . abstract ,或者完全省略修饰符. 类里各成员之间的定义顺序没有任何影响,各成员之间可以相互调用,需要指出的是,static 修饰的成员不能访问没有 static 修饰的成员. 构造器用于构

LeetCode每日一题(五):加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一. 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字. 你可以假设除了整数 0 之外,这个整数不会以零开头. 示例 1: 输入: [1,2,3]输出: [1,2,4]解释: 输入数组表示数字 123.示例 2: 输入: [4,3,2,1]输出: [4,3,2,2]解释: 输入数组表示数字 4321 来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/plus-one 思路:

LeetCode每日一题(一)

1103.分糖果Ⅱ 题目: 排排坐,分糖果. 我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友. 给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果. 然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果. 重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果.注意

LeetCode每日一题(三)

1.两数之和 题目: 给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中同样的元素. 思路: 由于哈希查找的时间复杂度为O(1),可以利用map降低时间复杂度. 根据与target的差对数组中的数字进行映射,当出现和为target的一组数时,马上就可以返回这组数. class Solution { public int[] twoSum(int[] nums, in

LeetCode每日一题(四)

1111.有效括号的嵌套深度 题目: 有效括号字符串: 仅由 ( 和 ) 构成的字符串,对于每个左括号,都能找到与之对应的右括号,反之亦然. 下述几种情况同样属于有效括号字符串: 1. 空字符串 2. 连接,可以记作AB,其中 A 和 B 都是有效括号字符串 3. 嵌套,可以记作(A),其中 A 是有效括号字符串 嵌套深度: 类似地,我们可以定义任意有效括号字符串 s 的 嵌套深度 depth(s): 1. s 为空时,depth("") = 0 2. s 为 A 与 B 连接时,de

【leetcode每日一题】25.Reverse Nodes in k-Group

题目: Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is. You may not alter the values in the nodes, only

【leetcode每日一题】149.Max Points on a line

题目:Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 解析:题目要求是给定n个2维的点,求出总共有多少个点在同一条直线上.由数学知识可知,给定三个点a,b,c,如果三个点在一条直线上,则a和b的斜率与c和d的斜率是相同的.用哈希表来做,针对每个点,求出其余所有点与它的斜率,找出最大值.然后遍历所有的点,返回最大值. 特别需要主意的几点:1)可能存

LeetCode每日一题(四):搜索插入位置

题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引.如果目标值不存在于数组中,返回它将会被按顺序插入的位置.你可以假设数组中无重复元素. 示例 1:输入: [1,3,5,6], 5输出: 2 示例 2:输入: [1,3,5,6], 2输出: 1 示例 3:输入: [1,3,5,6], 7输出: 4 示例 4:输入: [1,3,5,6], 0输出: 0 思路:二分查找的变体,正常二分查找,寻找不到返回的是-1,此题可以返回二分查找的left边界,即为插入地址. 代码: cla

LeetCode每日一题

994. 腐烂的橘子 解法一:DFS 1 int dx[4]={-1,0,1,0}; 2 int dy[4]={0,-1,0,1}; 3 class Solution { 4 public: 5 //这里需要对普通的DFS进行一定的处理,即DFS进化版本... 6 void dfs(vector<vector<int>>& grid,int i,int j,int res){ 7 if(i<0||j<0||i>=grid.size()||j>=gri