最近給組裏的小夥伴作了一次培訓,分享了一些編程的基礎和自我學習編程的方法。感受對你們可能有用,裏面頗有很面試常常問到的基礎相關的東西,想分享給大夥。java
首先看三段代碼,分別爲JS代碼、C#代碼和Java代碼。有:局部變量、全局變量,值類型和引用類型,靜態和常量、線程(JS沒有)。node
// 定義了一個Person類
function Person(id,name,age){
this.id = id;
this.name = name;
this.age = age;
}
function test(){
// 局部變量
let id = 0;
console.log(id);
}
// 全局變量
var person = new Person(100,'梁先生',25);
test();
複製代碼
// 定義了一個Person類
public class Person {
// 常量
public const string CodeTest = "梁先生";
// 靜態變量
public static int Code = 0;
// 私有變量
private int testId;
public void add(){
// 局部變量
int temp = 0;
testId++;
}
}
// 線程類
public class Count implements Runnable{
Person person;
public Count(Person person){
this.person = person;
}
@Override
public void run(){
person.add();
}
}
var person = new Person(100,'梁先生',25);
// 多線程的問題
int threadCount = 100;
for(int i = 0; i<threadCount;i++){
ThreadMsDeal = new Thread(new Count(person));
ThreadMsDeal.Start();
}
複製代碼
// 定義了一個Person類
public class Person{
// 常量
public const string CodeTest = "梁先生";
// 靜態變量
public static int Code = 0;
// 定義了一個私有變量
private testId = 0;
public Person(id,name,age){
this.id = id;
this.name = name;
this.age = age;
}
public int id {get;set;}
public string name{get;set;}
public int age{get;set;}
public void test(){
// 局部變量
int temp = 0;
testId++;
}
}
var person = new Person(100,'梁先生',25);
// 多線程的問題
int threadCount = 100;
for(int i = 0; i<threadCount;i++){
ThreadMsDeal = new Thread(new ThreadStart(test));
ThreadMsDeal.Start();
}
複製代碼
上面代碼中的數據類型都能在下方的圖找到對應。 git
JVM和CLR和很相似,使用JVM來講明 github
js的V8虛擬機和上面兩種有點像,可是又很不相同:JavaScript中全部的數據都是存放在堆內存中,爲了基本數據類型和引用數據類型的理解區分開來。面試
值類型和引用類型 算法
Q:爲何阿里巴巴Java開發手冊不建議在for循環中使用「+」進行字符串拼接? 數據庫
A:String是一個對象,且對象一旦被建立就是固定不變的了,對String對象的任何改變都不影響到原對象,全部一直在建立和銷燬對象,改變指針的指向,浪費內存和性能。咱們可使用StringBuilder和StringBuffer,他們是可變的。編程
GC垃圾回收c#
垃圾回收的幾種方式 設計模式
**GC調優:**不少次面試的時候有問道,gc垃圾回收的調優,你又瞭解嘛?
線程不安全
線程不安全:就是不提供數據訪問保護,有可能出現多個線程前後更改數據形成所獲得的數據是髒數據。
Q: i++是線程安全嗎? A: 是不安全的
每一個線程都有本身的工做內存,每一個線程須要對共享變量操做時必須先把共享變量從主內存 load 到本身的工做內存,等完成對共享變量的操做時再 save 到主內存。好比上面的i,是先拷貝到線程中的,而後作完++之後,不是很及時的刷新到主存。
問題就出在這了,若是一個線程運算完後還沒刷到主內存,此時這個共享變量的值被另一個線程從主內存讀取到了,這個時候讀取的數據就是髒數據了,它會覆蓋其餘線程計算完的值。
線程安全必要條件
原子性:跟數據庫事務的原子性概念差很少,即一個操做(有可能包含有多個子操做) 要麼所有執行(生效),要麼所有都不執行(都不生效)。
可見性:當多個線程併發訪問共享變量時,一個線程對共享變量的修改, 其它線程可以當即看到。可見性問題是好多人忽略或者理解錯誤的一點。
順序性:是程序執行的順序按照代碼的前後順序執行。
原子性解決:保證操做原子性的工具是鎖和同步方法(或者同步代碼塊)。使用鎖,能夠保證同一時間只有一個線程能拿到鎖,也就保證了同一時間只有一個線程能執行申請鎖和釋放鎖之間的代碼。 Java中的synchronized、c#中的lock
可見性解決:volatile關鍵字來保證可見性。當使用volatile修飾某個變量時,它會保證對該變量的修改會當即被更新到內存中,所以其它線程須要讀取該值時必須從主內存中讀取,從而獲得最新的值。
高併發,目前比較流行的有兩種,一種爲多線程的方式(java中後臺就是這樣的),一種是事件驅動,異步IO(node中是這樣的)。
高併發-多線程
線程的在操做系統的調度中,某個時刻,只能將資源讓給一個線程。而線程的上下文的切換,會有很大的消耗。
高併發-異步IO
只有一個線程,沒有上下文的切換消耗,可是隻有一個線程幹活。
選擇場景若是 IO時間多,那異步IO效率高,選擇場景若是 計算時間多,那同步IO效率高。Nginx 和 Node就是基於這種模式
經常使用的數據結構
時間複雜度
這個方法須要 (n + 1 + n + 1) = 2n + 2 次運算。咱們通常會把係數和尾巴去掉,由於在n趨近無窮大的時候,係數和尾巴都不咋起做用,差不了多少。故:時間複雜度爲:O(n)
void Func(int n) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
printf("Hello, World!\n");
}
}
}
複製代碼
時間複雜度爲:O(n × n × 1),即 O(n^2)。:
數據結構的時間複雜度
時間複雜度的圖像,根據本身的編碼狀況選擇對應的數據結構
你們能夠對比一下,在x軸趨於無窮大時,x爲數據量,而O(1)和log(n)的y值還只是1或者很小很小,y爲查詢的操做的次數。因此你們能夠選擇必定的數據結構來優化本身的代碼
在編碼的時候去理解和運用一下三個特徵。由於培訓例子是舉的工做中的例子,不咋好拿代碼出來說解。在咱們發現有相同的代碼的時候,咱們是能夠抽象出來的。還有一個是:對接口編程,而不是對實現編程。
五大原則也是須要必定的編碼去支撐,而後才能本身的理解
還有經常使用的設計模式,這個也是很重要的,須要掌握經常使用的,由於時間問題,不在這裏講解。
這個是我的總結,和語言無關,和框架無關。我的仍是以爲基礎仍是很重要的,若是你懂基礎,在代碼優化,和出問題的時候去排查,站的角度是不同的。
可能涉及到的書或者知識點比較多,可是沒有必要全看完整的東西 ,只須要掌握基礎,經常使用的知識點便可。不經常使用的東西,或者都不會涉及到的東西就不用看。
不少如代碼重構,優化,是能夠一直貫穿在整個圖中的。
下方是是我的的一些學習方法和常常看到一些知識點的出處。
但願能幫助到你們,謝謝!
github.com/liangwei010… 能夠的話,給個星星哦!