----------- android教育訓練、java教育訓練、java學習型技術部落格、期待與您交流! ------------
----------- Java基礎練習-思維-糾錯-記錄彙總(持續跟新) ------------
(即使是最幼稚的知識點或思維也要仔細對待)
(以後跟新到代碼記錄貼中)
Note1.關于循環的條件判斷思路
場景:
在進行練習判斷一個數是否為素數的過程中,遇到這樣的情況:定義int型n,打算用for循環周遊n次,判斷n在模2到n-1過程中是否都得0。若為素數,則隻有模1和模其本身會得0。是以在寫for循環時,當滿足取模後等于0,還得繼循環曆直到循環結束後判斷是否結果都為0,是才為素數。這樣讓我糾結很長時間。
分析:
在需要循環是,會遇到雖然需要循環的次數是确定的,但判斷條件較難描述,需要完全循環n次後判斷符合判斷條件的循環是否為m。那麼就可以借助這個m來進行輔助判斷。
在上面的素數判斷過程中,相當于m=n-2,條件為n%i==0(i是為确定循環次數定義的增量)。或者相當于m=2,條件為n%i!=0。
是以就可以用兩個嵌套循環或者外層用選擇内層循環來達到需要的目的。
代碼:
思路初步代碼:
/*
題目:判斷101-200之間有多少個素數,并輸出所有素數。
*/
class Prime{
//對這個數某1,和某自己同時為0,否則不是
public static void main(String[] args)
{
int sum=0;
for (int n=101;n<=200;n++)//判斷素數
{
int m=0;
for (int i=1;i<=n;i++)
{
if (n%i==0)
{
m++;
}
}
switch (m)
{
case 2:
sum++;
if(sum%10==0)//若列印10個後換行繼續列印
{
System.out.println(n+" ");
}else
System.out.print(n+" ");
}
}
System.out.println("共有"+sum+"個素數");
}
}
整理優化代碼:
//判斷素數
private static boolean isPrime(int n)
{
boolean flag = true;
if(n==1)
flag = false;
else
{
for(int i=2;i<=Math.sqrt(n);i++)
{
if((n%i)==0 || n==1)//模1,模自身為0。
{
flag = false;
break;
}
else
flag = true;//目前循環為真。但由于并且沒有break,是以會繼續直到循環結束,如果依然這樣,就是代表所有的每次循環都為真。
}
}
return flag;//周遊結束了,依然為真。
}//這裡的判斷,是考慮特殊情況1。
—————————————————————————---------------------------------------———————————————————————————————————————
Note2.判斷是否素的數循環次數問題
問題描述:
判斷是否素數在定義循環時,循環體的循環次數定義資料判斷表達式為為i=2;i<=Math.sqre(n);i++.為什麼?
分析:
循環中是來判斷這個數能不能被拆分成兩個整數數相乘。但是從1開始取模,當取值取到這個數的開方,和面如果出先取模等于0的情況就會跟前之前未出現取模等于0沖突,是以是垃圾運算。
舉例:
21=3*7--->在之前判斷出3之後,就不用判斷之後的7*3了。極端情況就是假設這個數是x*(x+1)。當判斷出x*(X+1)的時候,後面就開始重複之前的判斷了。
代碼:
for(int i=2;i<=Math.sqrt(n);i++)
—————————————————————————---------------------------------------——————————————————————————————————————
Note3.轉義字元的使用
Java中的列印輸出語句中,對某些字元賦予了特殊含義。是以可以通過\ 反斜杠來轉變其後面字母或者符号在輸出語句中的含義。
\n:換行
\t:制表符(Tab鍵)
\b:倒退(Backspace)
\r: 回車(回到本行行首并替換第一個字元)
或者用反斜杠來讓一個原本被Java賦予了特殊含義的字元成為普通字元列印出來。
\" \' \( \{ \[等。
—————————————————————————---------------------------------------——————————————————————————————————————
Note4.标号輔助嵌套循環指定循環控制
循環嵌套中,可以事先給每層循環命名。這樣,用标号可以輔助循環嵌套中的執行流程控制。
格式:
a1:for() 或者 a2:while()
并在需要使用break或者continue時後面加上需要傳回或跳出那層循環名。例如:
break a1;
continue a2;
注意:break和continue隻能出現在switch和循環中。他們的差別是break跳出循環,continue隻是提前結束本次循環進入下次循環。
—————————————————————————---------------------------------------——————————————————————————————————————
Note5.關于基本資料類型和引用資料類型參數傳遞問題
基本類型:形式參數的改變對實際參數沒有影響。
引用類型:形式參數的改變直接影響實際參數。
基本資料類型,在一個方法當中定義後,隻要不在本方法中對這個定義了某種基本資料類型的變量改變他的實際參數(也就是指派,無論是隐式的還是直接指派語句指派)即使在其他調用了他的方法函數中對他的實際變量進行了更改,那麼他隻要形式參數不改變,那麼他的的實際參數也就是他值也就就不會變。
而引用資料類型,在一個方法中定義,雖然不在本方法中對這個對這個引用資料類型的變量,雖然在本類中他的實際參數不改變,但在某個調用了它的方法中對他的實際參數進行了更改,那麼在這個定義此變量的函數中,他的實際參數也會随之改變,因為他們同樣指向一個位址值,操作同一個位址的資料。
—————————————————————————---------------------------------------——————————————————————————————————————
Note6.關于成員變量和局部變量
定義位置:局部變量定義在方法中或者方法聲明中。成員變量定義在類中,方法外。
存儲位置:局部變量是存儲在棧記憶體中的。成員變量是存儲在對記憶體中建立對象是所開辟的空間中的。
初始化值:局部變量是必須在定義後給其賦初始化值的才能使用。成員變量則在定義後由系統預設初始化,然後再由程式顯示初始化。
聲明周期:局部變量是與方法的調用與結束同步存在的。而成員變量則是同對象的建立消失同步存在的。
—————————————————————————---------------------------------------——————————————————————————————————————
Note7.遇到循環不好建立标準循環格式或者函數來表達時的做法
再碰到不好建立循環的時候,可以列出一定數量的執行個體,将規律執行個體化,書面化。然後再在列出的執行個體中總結規律,找出标準格式。
案例:
/*
需求:小于8的整數。先倒序,然後每位數字加上5,在用和除10的餘數代替該數字。最後将第一位最後一位互換。
請任意指定一個小雨8的整數,并将加密後結果列印。
思路:
用數組存儲每位上的數字,并倒序。
每位數字進行加,取模操作,并更新數組中元素。
對數組中首,尾元素互換。
周遊數組元素輸出。
*/
import java.util.*;
class ShuziJiami
{
public static void jiaMi(int num)
{//用數組存儲每位上的數字,并倒序。
int[] arr=new int[8];//定義一個數組。
//指派
//arr[0] = number/10/10/10/10/10%10;
//System.out.println(arr[0]);
//arr[1] = number/10/10/10/10%10;
//arr[2] = number/10/10/10%10;
//arr[3] = number/10/10%10;
//arr[4] = number/10%10;
//arr[5] = number%10;
//改進版
//定義一個索引從0開始
/*
int index = 0;
arr[index++] = number/10/10/10/10/10%10;
//System.out.println(arr[0]);
arr[index++] = number/10/10/10/10%10;
arr[index++] = number/10/10/10%10;
arr[index++] = number/10/10%10;
arr[index++] = number/10%10;
arr[index++] = number%10;
*/
/*
//改進版
int index = 0;
while(number>0)
{
arr[index++] = number%10; //擷取個位,擷取十位...
number/=10; //number值變化為除以10,
*/
/*
第一次:
number=123456,index=0
arr[0]=6; index=1,number=12345
第二次:
index=1,number=12345
arr[1] = 5; index=2,number=1234
...
arr[0] = 6
arr[1] = 5;
arr[2] = 4
...
index = 6
number = 0
*/
}
//數組周遊
/*
for(int x=0; x<index; x++)
{
System.out.println(arr[x]);
}
*/
int index=0;
System.out.print("您輸入的原始密碼為:"+num);
while (num>0)
{
arr[index++]=num%10;
//arr[index]=num%10;
//index+=1;
num/=10;
}//每位數字進行加,取模操作,并更新數組中元素。
for (int i=0;i<index;i++)
{
arr[i]+=5;
arr[i]%=10;
}//對數組中首,尾元素互換。
arr[0]=arr[0]^arr[index-1];
arr[index-1]=arr[0]^arr[index-1];
arr[0]=arr[0]^arr[index-1];
System.out.print("\n加密後密碼為:");
for (int i=0;i<index;i++)//周遊數組元素并輸出。
{
System.out.print(arr[i]);
}
}
public static void main(String[] args)
{
Scanner in=new Scanner(System.in);
while (true)
{
System.out.println("請輸入原始小于八位密碼:");
int num=in.nextInt();
jiaMi(num);
System.out.println("\n"+"是否繼續?1:繼續,2:退出");
int x=in.nextInt();
if (x==1)
continue;
break;
}
}
}
這條note果然重要,今天又遇到個問題,結果用直接尋找規律建立方法來做,很容易想錯。後來幹脆列出來一系列執行個體,立馬解決。下面放代碼:
/*
題目:古典問題:有一對兔子,從出生後第3個月起每個月都生一對兔子,小兔子長到第三個月後每個月又生一對兔子,假如兔子都不死,問每個月的兔子對數為多少?
分析:用直接尋找規律來建立函數或者循環等來解決這個問題相當容易把自己想當機。是以通過執行個體法。
思路:
1.列出前些天每天兔子的個數:
第1天:1,
第2天:1,
第3天:2,
第4天:3,
第5天:5,
第6天:8,
第7天:13,
第8天:21....
2.這樣,相當容易就能發現數列的規律,那就是第n天的兔子對數,為前兩天之和。是以,想知道第n天的兔子的對數,隻要從1,2,3。。。開始累加就可以了。
3.代碼實作
*/
import java.util.*;
class Rabbit
{
static int getRabbit(int n)
{
if (n==1||n==2)
return 1;
return getRabbit(n-2)+getRabbit(n-1);
}
public static void main(String[] args)
{
Scanner in=new Scanner(System.in);
while (true)
{
System.out.println("請輸入想查詢兔子對數的月份:");
int n=in.nextInt();
System.out.println(n+"兔子為"+getRabbit(n)+"對");
System.out.println("是否繼續查詢,任意數字繼續,1:退出。");
int x=in.nextInt();
if (x==1)
break;
continue;
}
}
}
—————————————————————————---------------------------------------——————————————————————————————————————
Note8.匿名對象作為參數進行傳遞&局部變量隐藏全局變量
局部變量隐藏全局變量:
就是指在一個類中的一個方法中,如果方法中和類的成員變量都定義了一個同名的變量s。那麼如果在這個方法中調用這個變量,如果不加this則預設操作的是這個局部變量s,想要通路成員變量的s,就必須在s前面加this.。
匿名對象作為參數進行傳遞:
這是想表達,匿名對象他随機建立的那個臨時對象的位址值作為一個參數進行傳遞。
案例:
/*
匿名對象:沒有名字的對象。
應用場景: 1.當隻對方法隻進行依次調用。
2.匿名對象可以作為實際參數進行傳遞。
*/
import java.util.*;
class Phone
{
Phone()
{}
Phone(Phone s)
{
s =new Phone();
}
void show(Phone s)//就是把位址值(他也是一種變量)作為參數
{
System.out.println(s);
}
}
class PhoneTest
{
public static void main(String[] args)
{
Scanner in=new Scanner(System.in);
while (true)
{
Phone w=new Phone();
Phone w2=new Phone(w);
w2.show(w2);
w.show(w);
new Phone().show(new Phone());//老師,這裡兩個new Phone()的匿名對象是同一個位址麼???(不是)
System.out.println("是否繼續?人以數字繼續,1:退出");
int i=in.nextInt();
if (i==1)
break;
continue;
}
}
}
—————————————————————————---------------------------------------——————————————————————————————————————
Note9.關于成員變量和局部變量
局部變量和成員變量的差別
1.定義位置:局部變量定義在方法中或者方法聲明中。成員變量定義在類中,方法外。
2.存儲位置:局部變量是存儲在棧記憶體中的。成員變量是存儲在對記憶體中建立對象是所開辟的空間中的。
3.初始化值:局部變量是必須在定義後給其賦初始化值的才能使用。成員變量則在定義後由系統預設初始化,然後再由程式顯示初始化。
4.聲明周期:局部變量是與方法的調用與結束同步存在的。而成員變量則是同對象的建立消失同步存在的。
—————————————————————————---------------------------------------——————————————————————————————————————
Note10.關于JAVA初期學習方法及遇到問題的處理
首先不得不承認,我可能有潔癖,或者說是是所謂的強迫症.比如,受不了玩單機RPG漏掉哪怕1個NPC不跟他對話而丢掉可能出現的隐藏劇情,受不了身邊東西雜亂無章,受不了早上起床某個流程沒做.是以,這個習慣在學習中,經常然我無所适從.
尤其在學習Java的過程中,Java本身就是一門在思維模式的要求上遠遠超過語言格式的語言(我是這麼認為的),是以在學習過程中,尤其是面對零基礎的入門者,初期必然遇到一大堆當時基本不太能完全了解透徹的問題.有時候,有原則的暫時忽略(有原則的,暫時性的)這些問題,雖然知道這樣,自己的心裡會有種心中打了疙瘩的感覺,但是,你沒有選擇.既然你推倒不了Java,就先讓Java推倒你吧,你要記得,總有一天,我們會把Java反推倒,進行逆襲100遍啊100遍......
是以,真心給跟我一樣,完美癖的童鞋們,我們戰略妥協吧,記得日後100遍啊100遍就行^v^.
而且,說實話,有些問題,在沒有完全學好Java的基礎知識了解起來的确比較費勁.日後會逐漸了解的...
此緻
敬禮(Java,請記住我日後的100遍啊100遍~~~~~~~~~~~~~~~~)
—————————————————————————---------------------------------------——————————————————————————————————————
Note11.關于多态的了解,用法等總結
之前的我自己的了解.
父類引用指向子類對象.這個是多态最常見的表現形式,可以這麼想.父類,子類,都是一個模具.成員變量,就像是可在模具上的資訊,成員方法就像是模具上各種大大小小的凹槽,用來填充料.
當生成一個父類引用,就是生成了一個父類的模具;此時如果指向一個子類對象,就是用子類的模具先做一個産品出來,然後再放到父類上塞進去.這個時候,因為子類是繼承父類的,父類的凹槽子類中肯定全有,而子類中還可能會多出更多凹槽,是以在子類産品塞上父類的模具的時候,對于那些子,父類都有的凹槽,子類産品是能塞進去的.沒有的就隻能暫且留在模具外面.而成員變量,就是文字,這樣,拿的誰的模具,文字就是誰的.這樣就是完整的解釋了:父類引用,指向子類對象了.
而在這個情況下,想調用子類特有的功能,就需要進行向下轉型.想,我們本來就是用子類模具做出來的産品,雖然在塞到父類模具中是,那些子類特有凹槽做出來的部分暫且留在父類模具外面,但是如果這個時候我們再把它塞回子類模具,怎麼樣,是不是就又全能塞進去了.這就是向下轉型,能夠使用子類特有方法了.
大神思維幫助了解.(借鑒了黑馬論壇某貼)
多态的根本原因在于父類引用變量在編譯時擁有一個類型,叫編譯時類型,在執行時有另一個類型,叫運作時類型。而這兩個類型可以相同,也可以不同(不相同:比如父類引用指向子類對象.)。一個引用變量的編譯時類型是指派表達式左邊的類型,運作時類型是指派表達式右邊的類型。
舉個例子:Animal ref = new Dog(....)
ref的編譯時類型是Animal。是以代碼中不能調用Animal中沒有的方法。假設Dog複寫了Animal中的某個方法。ref的運作時類型是Dog,運作的就是Dog複寫後的方法。
那為什麼super關鍵字可以調用父類“未被複寫的”方法呢?我覺得複寫這個詞很具有誤導性。記憶體中複寫前和複寫後的方法都存在,不是你死我活的關系,應該叫“多态”方法更準确些。super調用了父類記憶體中的方法,表現給我們看的效果就是“複寫前”。this調用了子類記憶體中的方法,表現給我們看的效果就是“複寫後”。自始至終你的每一行代碼都載入了記憶體,沒有被JVM吃掉,隻是JVM它選擇性的挑了一個給你看。
一句話總結,複寫沒有改變任何東西,隻是多了一個選擇而已。隻不過因為一般隻能向上轉換,也就是運作時類型是編譯時類型的子類,看上去引用變量調用的隻能是子類的“複寫後”方法,“被複寫”的方法“似乎”被消滅了。其實它還頑強滴活在父類的記憶體裡,一個super就能調出來.
關于子父類,複寫方法,細究.
案例:
class Fu
{
static int number=20;
int num=2000;
void method()
{
System.out.println("Fu\t"+num);
}
void show()
{
System.out.println("Fu\t"+number);
}
}
class Zi extends Fu
{
static int number=10;
int num=1000;
void method()
{
System.out.println("Zi\t"+num);
}
void show()
{
System.out.println("Zi\t"+number);
}
}
class Test
{
public static void main(String[] args)
{
Fu test=new Zi();
test.show();
test.method();
System.out.println(test.number);
System.out.println(test.num);
}
}
//----------------------------------------
/*
Zi 10
Zi 1000
20
2000
請按任意鍵繼續. . .
*/
這個結果很好的可以給我拿來思考.很明顯,這樣建立的對象test我是拿父類的引用,建立的子類對象.他的成員變量,不管是不是靜态,都是父類的值.(後面兩行的結果:20,2000)
而看到方法,輸出的是子類複寫了的内容,這裡,我起初有點迷糊.因為子父類具有同名變量.剛開始以為前兩行應該輸出:Zi 20,Zi 2000.很明顯,在複寫過程中,方法中調用變量還是遵循了就近原則,是以子類複寫父類方法,是單獨将子類的方法在子類中調用操作,将操作完成後的子類方法給了對象.而不是我之前以為的Zi類複寫方法中調用的是Fu類的num和number.
總結就是:多态中涉及到的跨類調用,除非用super,否則都是本類中調用,是誰的方法,就在誰的類中找變量,當然,子類如果在本類中找不到某些變量,可以去父類中再找一下.
—————————————————————————---------------------------------------——————————————————————————————————————
Note12.關于繼承與實作
(is a,like a很好麼?為什麼大家都拿這個用?)
其實我也不知道誰發明的這個:
is a
like a
我覺得反而讓新手了解更難...我就是其中一個.....
根本沒必要拽英文啊.
繼承,就是繼承一個父類,把子類看作父類一樣的事物,子類是父類範疇的一員,就說子類繼承了這個父類.
實作,就是實作一個接口,把接口看作一個描述,一個類是符合了接口的描述的一種事物,就說這個類實作了這個接口.
打個比方,門口貼的招聘啟示,要招一個20歲以下,男,170cm以上的保安.可以把這個招聘啟事當作一個接口,來這裡應聘的是不是都是實作了這個招聘啟事的要求的人?就說來的都是實作了這個招聘啟事接口的類.
那好,等你應聘進去了,成為了一個保安,是不是就是成為了保安這個群體的一員?這就了解為你這繼承了保安這個類.
我暫且這麼了解,期待以後跟新思維.
—————————————————————————---------------------------------------——————————————————————————————————————
Note13.關于重寫hashCode方法.
在HashSet,HashMap,Hashtable中,都是用到了hashCode()方法,根據自己的需求,将hashCode方法進行重寫,來保證我們集合中元素的唯一性. 這裡,就出現了個問題.我重寫了hashCode方法,然後将某些同類型對象存儲進入集合中.随後,其中某個對象的成員屬性進行了更改... 然後就詭異了...如果想删除該元素,就沒把法了通過remove來删除了.
{
public static void main(String[] args)
{
Collection<Student> col = new HashSet<>();
Student s1 = new Student("gll1",123);
Student s2 = new Student("gll2",223);
Student s3 = new Student("gll3",323);
col.add(s1);
col.add(s2);
col.add(s3);
s1.age = 323;
for( Student temp : col){
System.out.println(temp);
}
System.out.println(col.remove(s1));
for( Student temp : col){
System.out.println(temp);
}
}
}
并且,再次添加該元素,結果無法保其唯一行了.這就是所謂的記憶體溢出... 是以,重寫了hashCode方法後,集合中對象的成員屬性最好别更改的!!!
—————————————————————————---------------------------------------——————————————————————————————————————
Note14.網絡程式設計裡容易忽略還找不到的小問題
如上圖的代碼,網絡程式設計中,最容易忽略的應該就是字元流是用到了readLine方法,而沒有仔細考慮到讀取後,會減少一個換行符的問題.
如果在上面的代碼裡面,沒有放newLine()那麼就會造成兩邊都阻塞在readLine代碼那裡了...
還有需要注意的就是,流的關閉問題.Socket的流隻有一次獲得,關閉的機會.是以,一個套接字,不能實作多次從中獲得輸入,輸出流的操作.
—————————————————————————---------------------------------------——————————————————————————————————————
-----------------------android教育訓練、java教育訓練、java學習型技術部落格、期待與您交流!----------------------
詳情請檢視:http://edu.csdn.net/